Codeforces Round #681 (Div. 2, based on VK Cup 2019-2020 Codeforces Round #681 (Div. 2, based on VK Cup 2019-2020 - Final)

传送门

B

题意:01串,消去连续的1费用为a,将一个0变成1费用为b,问消除所有1的最低费用。

题解:对于每段连续1计算与前一个段的距离len,如果费用len*b<a,选择把一段0变成1,否则直接消去连续的1。

#include<iostream>
using namespace std;
int t,a,b;
string s;
int main(){
    cin>>t;
    while(t--){
        cin>>a>>b;
        cin>>s;
        int p=0;
        for(int i=0;i<s.length();i++){
            if(s[i]=='1'){
                p=i;
                break;
            }
        }
        int ans=0,ok=0,len=1e5+7;
        for(int i=p;i<s.length();i++){
            if(s[i]=='1'){
                if(ok==0){
                    ans+=min(a,len*b);
                    ok=1;
                    len=0;
                }
            }
            if(s[i]=='0'){
                ok=0;
                len++;
            }
        }
        printf("%d
",ans);
    }
}

C

题意:n个菜,可以自己做,也可以点外卖,自己做要a[i]时间,点外卖b[i]时刻送达,最快最久完成n道菜。(自己做累加时间,点外卖取最长时间)

题解:对于一个某个菜我选择点外卖假如10分钟送达,如果另一个菜点外卖的话9分钟送达,那么我肯定两个菜都点外卖,因为第二个菜不会占用我任何时间,那么我们对外卖的送达时间排序按小到大排序,枚举前i个菜都点外卖的情况,后n-i个菜自己做(用前缀和优化)的时间,更新ans。

#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
int t,n;
struct madoka{
    ll a;
    ll b;
}ma[200007];
ll pre[200007];
bool cmp(madoka a1,madoka a2){
    if(a1.a==a2.a){
        return a1.b>a2.b;
    }
    else{
        return a1.a<a2.a;
    }
}
int main(){
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>ma[i].a;
        }
        for(int i=1;i<=n;i++){
            cin>>ma[i].b;
        }
        sort(ma+1,ma+1+n,cmp);
        pre[n+1]=0;
        for(int i=n;i>=1;i--){
            pre[i]=pre[i+1]+ma[i].b;
        }
        ll ans=1e18+7;
        for(int i=0;i<=n;i++){
            ans=min(ans,max(ma[i].a,pre[i+1]));
        }
        printf("%lld
",ans);
    }
}

D

题意:n个数,每次可以选择前或后若干个数都减一,问能否全0;

题解:对于一个递减序列,我当然能完成

​ 如:5 4 3 2 2 >> 4 4 3 2 2 >> 3 3 3 2 2 >> 2 2 2 2 2 >> 0 0 0 0 0 ;

只要把前一个变成于后一个一样,之后再一起减1就好了。(递增同理)

​ 先递减再递增也当然可以,拆开同上操作即可,问题就是,无峰函数怎么判断是否合法,其实也不难,首先先找前第一段递减区间,同上操作,剩下的区间我只需考虑如何尽可能让它递增即可,在区间的值不能增加情况下,让区间尽可能递增,只能让尽可能让每一个位置的前一个位置的值尽可能小。

​ 如:6 5 4 5 4 5 3

​ 找到递减区间 654,

​ 那么省下5 4 5 3,我就让前一个位置值尽可能小,5最多可跟着前面的值减4,到1。

​ 省下 1 4 5 3,前一个位置减了4,但要满足递增,4最多减3,到1,

​ 省下 1 1 5 3,前位置减了3,5可以减3,到2,

​ 省下 1 1 2 3,最后序列递增输出YES,否则NO。

#include<iostream>
using namespace std;
int t,n,a[30007];
int main(){
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        int p=n,now=0,ok=1;
        for(int i=2;i<=n;i++){
            if(a[i]>a[i-1]){
                p=i;
                now=a[i-1];
                a[i]-=now;
                break;
            }
        }
        for(int i=p+1;i<=n;i++){
            if(a[i]<a[i-1]){
                ok=0;
            }
            else{
                int lin=a[i]-a[i-1];
                now=min(lin,now);
                a[i]-=now;
            }
        }
        if(ok)printf("YES
");
        else{
            printf("NO
");
        }
    }
}