Codeforces Round 935 (Div. 3)

题目链接

A. Setting up Camp

题意

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

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

思路

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

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

代码

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 m+1 m+1分钟,即若烟花在 t t t时刻被点燃,那么在 t , t + 1 , . . . , t + n t,t+1,...,t+n t,t+1,...,t+n 时刻都被视为在天空中的时间。现在问天空中最多同时出现多少个烟花。

思路

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

代码

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

题意

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

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

思路

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

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

代码

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

思路

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

代码

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()

E. Binary Search

题意

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

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

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

最终的 p l p_l pl即为答案

思路

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

代码

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, v 1 , v 2 , . . , v n v_1,v_2,..,v_n v1,v2,..,vn 表示每个蘑菇的价值。现在你可以采摘其中一些蘑菇,来获得蘑菇总数乘以采摘蘑菇中最小价值 的总价值。接下来还有一个排列p, p 1 , p 2 , . . . , p n p_1,p_2,...,p_n p1,p2,...,pn ,并且若你采摘了k个蘑菇,那么下标为 p 1 , p 2 , . . , p k − 1 p_1,p_2,..,p_{k-1} p1,p2,..,pk1 的蘑菇的价值就会变为0,也就是说你将不能采摘这些蘑菇。

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

思路

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

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

代码

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

#include
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;
}

你可能感兴趣的:(算法,数据结构,c++)