题目链接

A. Setting up Camp

题意

有一些屋子,每个屋子最多容纳三个人,有三种人,内向人必须一个人一个屋,外向人必须三个人同时一个屋子,综合人随意(一个两个三个都可)

现在有$a$ 个内向人, $b$ 个外向人, $c$ 个综合人。 问最少需要多少个屋子才能满足所有人的要求,如果无论如何都不能满足那么就输出-1

思路

我们很容易发现, 对于内向人,不会导致输出-1.因为给他们一人一个屋子即可。 对于综合人同样不会导致输出-1. 只有当外向人在引入几个综合人之后,仍然无法成为3的倍数时,会导致出现-1的情况。 换成代码语言即为$ b\%3+c<3$

只要不是输出-1 的情况,我们的答案即为$a+ \lceil \frac{b+c}{3}\rceil$

代码

1
2
3
4
5
6
7
8
9
10
11
def solve():
pass
a,b,c = [int(x) for x in input().split()]
if b%3 != 0 and b % 3 + c < 3 :
print(-1)
return
print((b+c+2)//3+a)
return
###########
for _ in range(int(input())):
solve()

B. Fireworks

题意

有两个烟花发射器, 第一个烟花发射器会每隔a分钟发射一个烟花,第二个烟花发射器会每隔b分钟发射一个烟花。

而每个烟花会在天空持续$m+1$分钟,即若烟花在$t$时刻被点燃,那么在$t,t+1,…,t+n$ 时刻都被视为在天空中的时间。现在问天空中最多同时出现多少个烟花。

思路

很明显,在刚开始时,A和B烟花会同时开始释放,此刻在$m+1$分钟内有可能放出最多的量的烟花。这段时间中,这两个发射器都在第1时刻放出了一个烟花, 之后的m分钟则会各自再放出$\frac{m}{a} , \frac{m}{b}$ 个烟花。于是我们便知道了总数为$2+\frac{m}{a} , \frac{m}{b}$

代码

1
2
3
4
5
6
7
8
9
10
def solve():
pass
a,b,m = [int(x) for x in input().split()]
ans = 2
ans += (m)//a
ans += (m)//b
print(ans)
###########
for _ in range(int(input())):
solve()

C. Left and Right Houses

题意

现在有一些居民$a1,a_2,…,a_n$ 排成一条线,他们各自意愿要住在路的左边或者右边。你需要修建一条路,修在$a_i$和$a{i+1}$的中间$i = 0,1,..,n-1,n$ 并且要求你对于路两边的居民,满足意愿的居民数量都大于等于不满足意愿的居民数量。

在这前提下,你要尽可能地将路修在中间,即$|\frac{n}{2} - i|$ 尽可能地小。如果两个最小的 $|\frac{n}{2} - i|$ 都满足,输出较小的那个。

思路

可以使用前缀和来记录满足意愿在左边的数量,这样如果路修在了i的位置,那么满足条件的表达式即为$x-sum[x]>=sum[x] \&\& sum[n]-sum[x]>=n-x-sum[n]+sum[x]$

我们直接枚举$|\frac{n}{2} - i|$ 的值,然后推算出两个$i$ , 分别判断是否满足情况。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def solve():
pass
n = int(input())
lst = [0]
lst += [int(x) for x in input()]
sum = [0]*(n+2)
ans = []
for i in range(1,n+1):
sum[i] = sum[i-1] + lst[i]
for x in range(0,n+1):
if x-sum[x]>=sum[x] and sum[n]-sum[x]>=n-x-sum[n]+sum[x]:
ans.append(x)
dis = ans
mn = 9999999
for i in range(len(ans)):
mn = min(mn,abs(n/2-ans[i]))
for i in range(len(ans)):
if(abs(n/2-ans[i]) == mn):
print(ans[i])
break

###########
for _ in range(int(input())):
solve()

D. Seraphim the Owl

题意

同学A想插队 ,他前面有n个人, 他想要查到前$m$人中 ,现在有两个数组 $a,b$ 。若同学A从位置$y$插队到位置$x$那么他就需要支付$a_x$的金币,以及给中间所有人$b_i(x<i<y)$ 个金币。他可以选择插队多次。 问最少需要花费多少金币才能够插到前m的位置。

思路

我们若最终想要插队到第i的位置,那么所有$i<j\le n$ 的人都需要支付一个$a_j$或者$b_j$ ,我们取其中的较小值。 而最后还要支付$a_i$ 给第i个人。因此我们预处理$c_i = min(a_i,b_i) , 1\le i \le n$ 然后求出c的后缀数组,这样我们就可以用$O(1)$ 的时间得到到达第i位置需要的最少的金币,设为 $ans_i$ 我们从1到m中找到最小的 ans即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def solve():
pass
n,m = [int(x) for x in input().split()]
a = [int(x) for x in input().split()]
b = [int(x) for x in input().split()]
c = []
for aa,bb in zip(a,b):
c.append(min(aa,bb))
ans = [0]*(n+1)
sum = [0]*(n+1)
for i in range(n-1,-1,-1):
sum[i] = sum[i+1] + c[i]
for i in range(n-1,-1,-1):
ans[i] = sum[i+1]+a[i]
print(min(ans[0:m]))
###########
for _ in range(int(input())):

solve()

题意

安东要按照二分搜索来从p这个排列中找x这个数,然他没发现这个排列并不是有序的。因此他可能最终找不到x这个数,现在需要你至多进行两次交换,来使得安东按照二分查找后可以找到$x$这个数。

二分策略如下:起初让$l = 1,r = n+1$

  1. 如果$r - l == 1$ 结束循环
  2. m = $\lfloor \frac{l+r}{2} \rfloor$
  3. 如果$p_m \le x $ ,$l = m$ ,否则$r =m $

最终的$p_l$即为答案

思路

很无趣的题目,直接按照题目中给出的二分方法找到最终的错误答案,然后将他和x所在的位置进行交换即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def solve():
pass
n,x = [int(x) for x in input().split()]
lst = [0]
lst += [int(x) for x in input().split()]
l = 1;r = n + 1
p = 0
for i,y in enumerate(lst):
if y == x :
p = i
break
while(1):
if(r - l == 1):
break
m = (l+r)//2
if lst[m]<=x:
l = m
else : r = m
print(1)
print(p,l)
###########
for _ in range(int(input())):
solve()

F. Kirill and Mushrooms

题意

有一些蘑菇,用一个数组v,$v1,v_2,..,v_n$ 表示每个蘑菇的价值。现在你可以采摘其中一些蘑菇,来获得蘑菇总数乘以采摘蘑菇中最小价值 的总价值。接下来还有一个排列p,$p_1,p_2,…,p_n$ ,并且若你采摘了k个蘑菇,那么下标为$p_1,p_2,..,p{k-1}$ 的蘑菇的价值就会变为0,也就是说你将不能采摘这些蘑菇。

问可采摘的最大价值是多少

思路

这场出现好多很无趣的题目,

使用一个标记数组$ba$记录哪些蘑菇不允许再采摘(一共三个状态,$ba[i]=0$表示可以采摘,$ba[i]=1$表示不允许采摘他了,$ba[i]= 2$表示被采摘过了。直接枚举采摘的数量,因为我们枚举的采摘数量越来越多,所以被ban掉的蘑菇只会越来越多。我们贪心地选择未被ban的蘑菇中,最大的那些即可。并且选择的这些蘑菇,同样可以在上一步所选中扩展而来,于是可以在$O(nlogn)$的排序过后就可以在$O(n)$ 的复杂度下选出答案。

代码

代码解释可以看代码中的注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
void solve(){
int n ;
cin>>n;
vector<int> v(n+5);
rep(i,1,n) cin>>v[i];
vector<int> p(n+5);
rep(i,1,n) cin>>p[i];
vector<PII> node;
for(int i = 1;i<=n;i++){
node.push_back({v[i],i});//将蘑菇和原始序号绑定
}
sort(node.begin(),node.end());reverse(node.begin(),node.end()); //升序排序
vector<int> ba(n+5);//ba数组
int ans = -1;//最终答案
int ansc = 0;//answer count最终答案要采摘的数量
int mn = 0;//min,较小值
int nowp = 0;//轮到采摘哪个蘑菇了
int nowc = 0;//现在采摘了多少个蘑菇了
for(int cnt = 1;cnt <= (n+1)/2;cnt++){//最多选择(n+1)/2个蘑菇
if(ba[p[cnt-1]] == 2){//如果之前有蘑菇被采摘,后来被ban了,那么应该扔掉它
nowc --;//即将nowc减一。(扔掉它之后一定不影响最小值,所以不需要更改其他变量)
}
ba[p[cnt-1]] = 1;//ban掉这一轮的蘑菇
while(nowp<n && nowc < cnt){//选取蘑菇直到足够cnt个
if(ba[node[nowp].second] == 1){//如果当前蘑菇被ban了就换下一个
nowp++;continue;
}
mn = node[nowp].first;//当前的值一定比mn原先的更小,所以直接赋值即可
nowc++;
if(ans<mn*nowc){//更新答案
ans = mn*nowc;
ansc = nowc;
}
ba[node[nowp].second] = 2;//这个蘑菇采摘过了
nowp++;
}
}
cout<<ans<<" "<<ansc<<endl;//输出答案

}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}