• Codeforces Global Round 7【ABCDE】(题解)


    目录

    涵盖知识点:思维、构造、马拉车、线段树。

    比赛链接:传送门

    D题只有数据范围的区别,故只写D2。

    好多题啊,随缘更新。(其实懒得写)

    A - Bad Ugly Numbers

    题意: 构造一个长度为(n)的数字使得其不能被其中的每一位数整除。
    题解: 2333333(雾)
    Accept Code:

    #include <bits/stdc++.h>
    using namespace std;
    
    int main(){
        int t;
        cin>>t;
        while(t--){
            int n;
            cin>>n;
            if(n==1){
                cout<<"-1
    ";
                continue;
            }
            else{
                cout<<2;
                for(int i=1;i<n;i++)cout<<3;
                cout<<"
    ";
            }
        }
        return 0;
    }
    

    B - Maximums

    题意: 对于数组(a),数组(x)满足(x_i = max(0, a_1, ldots, a_{i-1}),x_1=0),数组(b)满足(b_i = a_i - x_i)。现在给定(b),要求反推(a)
    题解: 顺着算一遍维护一下最大值加一下就好了。
    Accept Code:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+10;
    typedef long long ll;
    ll a[maxn];
    int main(){
        int n;
        cin>>n;
        ll x=0;
        for(int i=0;i<n;i++){
            cin>>a[i];
            a[i]+=x;
            x=max(x,a[i]);
            cout<<a[i]<<" ";
        }
        return 0;
    }
    

    C - Permutation Partitions

    题意: 给定一种([1,n])的排列,现在让你分成(k)块,求每块的最大值之和。形式上为(sumlimits_{i=1}^{k} {maxlimits_{l_i leq j leq r_i} {p_j}}),并算出有多少种分法可以达到这个最大值。
    题解: 最大值就是([n-k+1,n])的区间和。根据这(k)个数来划分区间即可,记录这(k)个数的下标,分法总数为(displaystyleprod_{i=1}^{k-1}(idx_{i+1}-idx_{i}))
    Accept Code:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+10,mod=998244353;
    typedef long long ll;
    int main(){
        int n,k;
        cin>>n>>k;
        ll ans=1,sum=0,l=0;
        for(int i=1,p;i<=n;i++){
            cin>>p;
            if(p>n-k){
                sum+=p;
                if(l)
                    ans=ans*(i-l)%mod;
                l=i;
            }
        }
        cout<<sum<<" "<<ans<<"
    ";
        return 0;
    }
    

    D2 - Prefix-Suffix Palindrome (Hard version)

    题意: 给定串(s),从(s)的前缀和后缀取任意长度进行拼接成新串(t),使得(t)为回文串并尽可能长。
    题解: 先双端扫描一遍,确定回文的前缀和后缀,获得左区间和右区间。然后再依次扫描每一个字母为中心的回文串,若回文串左端探入左区间或者右端探入右区间,判断并更新答案。D1可以应该可以暴力判回文,D2利用马拉车获得的(len)数组优化计算一下就可以了。具体计算看代码实现。
    Accept Code:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=1e6+10;
    string s,t;
    int len[maxn<<1];
    void manacher(){
        int center,right=0,radius;
        for(int i=1;i<t.length()-1;i++){
            if(i>=right)radius=1;
            else radius=min(right-i+1,len[center*2-i]);
            while(t[i+radius]==t[i-radius])radius++;
            if(i+radius-1>right)right=i+radius-1,center=i;
            len[i]=radius;
        }
    }
    int main(){
        int cas;
        cin>>cas;
        while(cas--){
            cin>>s;
            t="@#";
            for(char i:s){
                t+=i;
                t+='#';
            }
            t+='$';
            manacher();
            int l=0;
            while(l<s.length()&&s[l]==s[s.length()-l-1])l++;
            if(l>=s.length()){
                cout<<s<<"
    ";
                continue;
            }
            int anslen=0,ansl=0,ansr=0;
            for(int i=2;i<=t.length()-3;i++){
                int pl=(i-len[i])>>1,pr=(i+len[i]-4)>>1;
                //cout<<pl<<" "<<pr<<"
    ";
                if(pl>pr)continue;
                if(pl<=l){
                    int nl=pr,nr=s.length()-pl;
                    //cout<<"1:"<<nl<<" "<<nr<<"
    ";
                    if(nl+1+s.length()-pl>=anslen&&nl<nr)
                        anslen=nl+1+s.length()-nr,ansl=nl,ansr=nr;
                }
                if(pr>=s.length()-l-1){
                    int nr=pl,nl=s.length()-pr-2;
                    //cout<<"2:"<<nl<<" "<<nr<<"
    ";
                    if(nl+1+s.length()-nr>=anslen&&nl<nr)
                        anslen=nl+1+s.length()-nr,ansl=nl,ansr=nr;
                }
            }
            //cout<<ansl<<" "<<ansr<<"
    ";
            string ans=s.substr(0,ansl+1)+s.substr(ansr);
            cout<<ans<<"
    ";
        }
        return 0;
    }
    

    E - Bombs

    题意: 给定([1,n])的排列(p,q),将(p)依次加入初始为空的集合(S)(q)的值表示第几次加入的值为雷。若加入的是雷就把当前集合最大值给炸了(先加再炸)。现在规定对于每一个(i),前(q_i)(不含,即((q_1,q_2,dots,q_{i-1})))个都是雷。求对于每一个(iin [1,n]),操作后集合中的最大值。
    题解: 首先雷越多,最大值一定不会变的更大,所以该序列一定为非递增序列。由于(q)描述的是下标,所以我们关心(p)加入集合的顺序。现在我们将(i)从最大值开始遍历,若没有任何的雷,那么最大值一定为(n),现在我们确定一个最大值后,就要关注雷的位置,也就是什么时候会把这个最大值给pop出来。具体实现可以通过区间最大值来进行。首先将区间([1,idx_i])的最大值加一,对于(pos)位最大值为(x)的条件为全区间最大值大于0,当该位最大值确定后,我们就要将区间([1,q_{pos}])的最大值减一。区间最大值的维护用线段树即可。详细过程看代码。
    Accept Code:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=3e5+10;
    int n,p[maxn],q[maxn],ans[maxn];
    int mx[maxn<<2],tag[maxn<<2];
    inline int ls(int x){return x<<1;}
    inline int rs(int x){return x<<1|1;}
    inline void upd(int x,int v){mx[x]+=v,tag[x]+=v;}
    void pushdown(int root){
        if(tag[root]){
            upd(ls(root),tag[root]);
            upd(rs(root),tag[root]);
            tag[root]=0;
        }
    }
    void update(int root,int l,int r,int ql,int qr,int v){
        if(ql<=l&&r<=qr){
            upd(root,v);
            return;
        }
        int mid=(l+r)/2;
        pushdown(root);
        if(ql<=mid) update(ls(root),l,mid,ql,qr,v);
        if(qr>mid) update(rs(root),mid+1,r,ql,qr,v);
        mx[root]=max(mx[ls(root)],mx[rs(root)]);
    }
    int main(){
        cin>>n;
        for(int i=1;i<=n;i++){
            int x;
            cin>>x;
            p[x]=i;
        }
        for(int i=1;i<=n;i++) cin>>q[i];
        int idx=0;
        for(int i=n;i>=1;i--){
            update(1,1,n,1,p[i],1);
            while(idx<n&&mx[1]>0){
                idx++;
                ans[idx]=i;
                update(1,1,n,1,q[idx],-1);
            }
        }
        for(int i=1;i<=n;i++){
            cout<<ans[i]<<" ";
        }
        return 0;
    }
    
  • 相关阅读:
    【ExtJS】关于自定义组件(一)
    【ExtJS】关于自定义组件
    特殊的css样式
    样式表笔记
    html 表单笔记
    图片热点和网页内嵌随笔
    快速网页
    HTML基础和表格
    HTML基础
    递归算法 笔记
  • 原文地址:https://www.cnblogs.com/charles1999/p/12531391.html
Copyright © 2020-2023  润新知