Educational Codeforces Round 95 (Rated for Div. 2) A-D

时间:2022-07-25
本文章向大家介绍Educational Codeforces Round 95 (Rated for Div. 2) A-D,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

A. Buying Torches

题意:

首先规定一个木棍和一个木炭可以组合成一个火把,再给出两种操作,操作一是可以用一根木棍去换根木棍,操作二是可以用根木棍去换一个木炭,初始时有一根木棍,问最少进行多少次操作可以得到个火把(组成火把的过程不花费操作次数,换句话说,只要有至少个木棍和个木炭即可达成目标)。

思路:

因为两个操作都是需要用木棍来衡量的,所以我们不妨将目标的个火把都转换为木棍,首先个火把需要个木棍和个木炭组成,而一个木炭需要个木棍,所以总共需要个木棍,而每次操作一用一根木棍去得到个木棍,实质上是增加个木棍,所以列得不等式此处求得满足不等式的最小的就是操作一的次数了,将木棍转换成煤炭还需要次操作二,所以本题答案就是了。下面讲一下如何求,显然满足单调性,所以一种方法就是直接进行二分求解,另一种方法就是通过移项,得到,那么最小的就是。

#include<iostream>
#include<cstdio>
using namespace std; 
int main(void){
    int T;
    long long x,y,k;
    cin>>T;
    while(T--){
        scanf("%lld%lld%lld",&x,&y,&k);
        printf("%lldn",((y+1)*k-1+x-2)/(x-1)+k);
    }
    return 0;
}

B. Negative Prefixes

题意:

给出一个长度为的数列,有一些位置被锁定了,对于未被锁定的位置,可以进行重新排列,对于重新排列后的数组记为,现在记录一个前缀和,我们需要找到一个最大的,使得,问如何对数组进行排列,可以使得最小。

思路:

比较明显的一个贪心是,对于未锁定的位置,将其按照降序排序是最优的,下面稍微证明一下:假设按照如此策略得到的前缀和,不难看出无论如何操作,是不变的,那么,,以此类推,我们的目标是为了让的出现位置尽量靠前,也就是让后面的数都尽可能大,观察上面前缀和公式的变形,不难看出让后面的数尽可能小是最优的,所以直接实现即可,实现的话我是用了一个优先队列减少码量,当然去找最小值时间复杂度也是可以的。

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int MAX_N=110;
int a[MAX_N],b[MAX_N];
int main(void){
    int T,n,i;
    cin>>T;
    while(T--){
        priority_queue<int>q;
        scanf("%d",&n);
        for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
        for(i=1;i<=n;i++){
            scanf("%d",&b[i]);
            if(b[i]==0)
            q.push(a[i]);
        }
        for(i=1;i<=n;i++){
            if(b[i])
            printf("%d ",a[i]);
            else{
                printf("%d ",q.top());
                q.pop();
            }
        }
        printf("n");
    }
    return 0;
}

C. Mortal Kombat Tower

题意:

给出个怪物,1表示高级的怪物,0表示低级的怪物,现在A和B组队去打怪,两个人轮流出招,B的能力比较强,可以随便杀死所有怪物,A的能力较弱,只能杀死低级的怪物,但是A可以使用一个道具用来杀死一个高级怪物,在每个人出招时,至少杀死面前的一个怪物,至多杀死面前的两个怪物,A先开始出招,问如何分配策略,可以使得使用的道具最少就能杀死个怪物。

思路:

贪心去想的话,每次碰到高级怪物让B去击杀肯定是更优的,但需要讨论的情况非常多,而且对于这种无后效性的最优解问题,是一个比较显然的dp问题。设为到了第i个位置,B杀死第i个怪物的最小代价,为到了第i个位置,A杀死第i个怪物的最小代价,那么答案就是了。考虑转移,因为A和B需要轮流上阵,所以dp1只能向dp2转移,同理dp2也只能向dp1进行转移,又因为每次至多击杀两个连续的怪物,所以分类讨论一下就好了,代码中由注释,结合注释应该更好理解。

#include<iostream>
#include<cstdio>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAX_N=201000;
int dp1[MAX_N],dp2[MAX_N],a[MAX_N];
int main(void){
    int T,n,i;
    cin>>T;
    while(T--){
        scanf("%d",&n);
        for(i=0;i<=n;i++)
        dp1[i]=dp2[i]=INF;
        dp1[0]=0;//因为A需要进行先手,所以将B初始化为0
        for(i=1;i<=n;i++){
            scanf("%d",&a[i]);
            dp1[i]=min(dp1[i],dp2[i-1]);//B来击杀第i只怪物
            if(i>=2)
            dp1[i]=min(dp1[i],dp2[i-2]);//B来击杀第i只和第i-1只怪物
            dp2[i]=min(dp2[i],dp1[i-1]+a[i]);//A来击杀第i只怪物,如果第i只怪物是高级怪物的话,需要使用道具
            if(i>=2)
            dp2[i]=min(dp2[i],dp1[i-2]+a[i]+a[i-1]);//A来击杀第i只和第i-1只怪物,同上需要注意道具的使用情况
        }
        printf("%dn",min(dp1[n],dp2[n]));
    }
    return 0;
}

D. Trash Problem

题意:

在一维坐标轴上给出堆垃圾,首先规定原本在 x 位置有一堆垃圾,若将其扫到 x - 1 或 x + 1 位置后,会有一个单位的贡献,再规定此时的贡献 ans 为,将所有的垃圾合并为不超过两堆的贡献,现在给出次操作,每次操作会添加一堆新的垃圾或者删除一堆已有的垃圾,每次操作后输出贡献 ans,需要注意的是,题目保证了每堆垃圾的位置都不会相同。

思路:

首先考虑如何快速计算出贡献,假设当前有堆垃圾,若想要合并为一堆的话,无论如何合并,都需要将个相邻的间隔都走一遍,类似的,如果想要合并为两堆的话,只需要遍历个间隔即可,对于同一个状态来说,用表示当前状态下的个间隔之和,若想去掉一个间隔后使得总答案最小,那么显然去掉的间隔越大越好,此时答案就呼之欲出了,我们需要维护的就是和当前个间隔中的最大值,每次贡献的答案就是两者之差了。再考虑如何维护,因为每次给出的垃圾都是在坐标轴上的位置,且我们最终需要维护的间隔,实质上是所有坐标排序后,相邻两个数之差,故我们可以维护一下整个升序的序列,每次在加入新的垃圾 pos 时,找到两个位置 lower 和 upper 分别代表小于 pos 的最大值和大于 pos 的最小值,在加入 pos 后,只会影响到这个间隔,考虑加入 pos 后会造成什么影响:

  1. lower - upper 之间的间隔消失。
  2. lower - pos 之间的间隔新增。
  3. pos - upper 之间的间隔新增。

如此维护一下和所有的间隔即可,删除的话同理,只不过需要将所有操作倒着再来一遍即可。下面考虑如何实现,因为每次操作的时间限制在了,所以我用multiset实现的,一个用来维护原序列,一个用来维护所有的间隔,以及一个全局变量,每次添加或删除一个数,只需要相应的在multiset上二分找到相应位置,维护好答案后相应的删除或增加即可,时间复杂度。值得一提的是,为了防止在查找时造成不必要的越界,可以在初始化时加入相应的哨兵节点,维护时特判一下即可。

#include<iostream>
#include<cstdio>
#include<set>
using namespace std; 
const int INF=0x3f3f3f3f;
const int MAX_N=201000;
multiset<long long>st1,st2;
multiset<long long>::iterator lower,upper,it,t;
long long sum;
void add(int pos){
    st1.insert(pos);
    lower=st1.lower_bound(pos);
    upper=lower;
    lower--;
    upper++;
    if(*lower!=-INF&&*upper!=INF){
        long long delta=*upper-*lower;
        sum-=delta;
        st2.erase(st2.find(delta));
    }
    if(*lower!=-INF){
        long long delta=pos-*lower;
        sum+=delta;
        st2.insert(delta);
    }
    if(*upper!=INF){
        long long delta=*upper-pos;
        sum+=delta;
        st2.insert(delta);
    }
}
void del(int pos){
    lower=st1.lower_bound(pos);
    upper=lower;
    lower--;
    upper++;
    if(*lower!=-INF){
        long long delta=pos-*lower;
        sum-=delta;
        st2.erase(st2.find(delta));
    }
    if(*upper!=INF){
        long long delta=*upper-pos;
        sum-=delta;
        st2.erase(st2.find(delta));
    }
    if(*lower!=-INF&&*upper!=INF){
        long long delta=*upper-*lower;
        sum+=delta;
        st2.insert(delta);
    }
    st1.erase(st1.find(pos));
}
int main(void){
    int n,m,i,op,pos;
    scanf("%d%d",&n,&m);
    st1.insert(-INF);//哨兵节点
    st1.insert(INF);
    st2.insert(0);
    for(i=1;i<=n;i++){
        int pos;
        scanf("%d",&pos);
        st1.insert(pos);
    }
    for(it=++st1.begin();it!=st1.end();it++){//扫出所有间隔
        t=it;
        t--;
        if(*t!=-INF&&*it!=INF){
            st2.insert(*it-*t);
            sum+=*it-*t;
        }
    }
    printf("%lldn",sum-*st2.rbegin());
    while(m--){
        scanf("%d%d",&op,&pos);
        if(op==1)
        add(pos);
        else
        del(pos);
        printf("%lldn",sum-*st2.rbegin());
    }
    return 0;
}

点赞的时候,请宠溺一点