• Educational Codeforces Round 51 (Rated for Div. 2) 1051


    A. Vasya And Password

    链接:http://codeforces.com/contest/1051/problem/A

    题目大意:给你一段字符串,要求你对其中连续的一段进行修改,以使得这个字符串同时包含大小写字母和数字,输出修改后的字符串。

    解法:

    首先检查原串里出现了几种字符:

    1.出现一种,只要将前两个字符修改为没有出现的字符。

    2.出现两种,找一个位置的字符进行修改为没有出现过的字符,并且要确认这个字符不是某个种类唯一的一个。

    但是实现起来还是有一定难度

    #include<cstdio>
    #include<cstring>
    char s[105];
    int main(){
        int t;
        scanf("%d",&t);
        while(t--){
            scanf("%s",s);
            int l=strlen(s),a1=0,a2=0,a3=0,b1=0,b2=0,b3=0;
            int mi=l;
            for(int i=0;i<l;++i)
                if(s[i]>='0'&&s[i]<='9') ++a1,b1=1;
                else if(s[i]>='a'&&s[i]<='z') ++a2,b2=1;
                else a3++,b3=1;
            if(b1+b2+b3==3) printf("%s
    ",s);
            else if(b1+b2+b3==2){
                if(!b1){ 
                    if((s[0]>='a'&&s[0]<='z'&&a2!=1)||(s[0]>='A'&&s[0]<='Z'&&a3!=1)) s[0]='1';
                    else s[1]='1';
                }
                else if(!b2){
                    if((s[0]>='0'&&s[0]<='9'&&a1!=1)||(s[0]>='A'&&s[0]<='Z'&&a3!=1)) s[0]='a';
                    else s[1]='a';
                }
                else if(!b3){
                    if((s[0]>='0'&&s[0]<='9'&&a1!=1)||(s[0]>='a'&&s[0]<='z'&&a2!=1)) s[0]='A';
                    else s[1]='A';
                }
                printf("%s
    ",s);
            }
            else{
                if(a1) s[0]='a',s[1]='A';
                else if(a2) s[0]='1',s[1]='A';
                else s[0]='1',s[1]='a';
                printf("%s
    ",s);
            }
        }
        return 0;
    }

    B. Relatively Prime Pairs

    链接:http://codeforces.com/contest/1051/problem/B

    题目大意:给你一段区间[l,r],这段区间内的所有整数组成一个集合,把集合中的所有整数两两分为一组,(集合元素数一定是偶数)要保证每一组的gcd均为1,若能输出Yes和分组情况(多解输出任意一个合法解),若不能输出no

    解法:

    首先不可能输出no

    因为i和i+1一定能够分为一组,

    反证法:假设a=gcd(i,i+1)>1那么i=b*a,i+1=c*a => 1=(c-b)*a 因为a,b,c均为整数所以该式子显然不成立

    然后就以上述方法分组就可以了

    #include<cstdio>
    int main(){
        long long l,r;
        scanf("%I64d%I64d",&l,&r);
        printf("YES
    ");
        for(long long i=l;i<=r;++i){
            printf("%I64d ",i);
            if((i-l)%2) printf("
    ");
        }
        return 0;
    }

    C. Vasya and Multisets

    链接:http://codeforces.com/contest/1051/problem/C

    题目大意:给定n个整数,把它们分成两个部分每个部分中数量为1的元素种数相同。能则输出Yes和分割情况,不能则输出No

    解法:

    首先用桶记录所有数,

    我们可以忽略那些数量为2的数,因为它们不会影响答案

    对于数量为1的数,如果有偶数个那么也可以使得两个部分平衡

    但是如果是奇数个那么就需要一个数量s>2的数划分为s-1,1进行平衡;

    #include<cstdio>
    #include<vector>
    using namespace std;
    char s[105];
    int t[105],a[105],tt[4];
    vector<int>to[105];
    int main(){
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            t[a[i]]++;
            to[a[i]].push_back(i);
        }
        for(int i=1;i<=100;++i)
            if(t[i]>2) tt[3]++;
            else tt[t[i]]++;
        if(tt[1]&1&&!tt[3]) printf("NO");
        else{
            printf("YES
    ");
            for(int i=1,j='A';i<=100;++i)
                if(t[i]==1){
                    s[to[i][0]]=j;
                    if(j=='A') j='B';
                    else j='A';
                }
            if(tt[1]&1)
                for(int i=1;i<=100;++i)
                    if(t[i]>2){
                        s[to[i][0]]='B';
                        break;
                    }
            for(int i=1;i<=n;++i)
                if(!s[i]) s[i]='A';
            printf("%s",s+1);
        }
        return 0;
    }

    D. Bicolorings

    题目大意:现在有一个大小为2*N的矩阵每个点可以染成黑或白色,相邻并且颜色相同的则为同一块,问总块数==k的染色方法有多少种(对998244353取模)

    解法:非常裸的dp,对于一行来说可能会有4种状态:黑黑,白白,白黑,黑白,分别标号为0设f[i][j][k]表示前i列块数为k最后一行状态为k的方案数

    转移的方式,可以自己写一个矩阵,因为不同的状态之间进行转移的时候会增加的块数不同。

    #include<cstdio>
    const int mod=998244353;
    int f[2][4][2005],a[4][4]={
    {0,1,1,1},
    {1,0,1,1},
    {0,0,0,2},
    {0,0,2,0}
    };
    int main(){
        int n,k,i,j,p,q,ans=0;
        scanf("%d%d",&n,&k);
        f[1][0][1]=f[1][1][1]=f[1][2][2]=f[1][3][2]=1;
        for(i=2;i<=n;++i)
            for(j=1;j<=i*2&&j<=k;++j)
                for(p=0;p<4;++p){
                    f[i&1][p][j]=0;
                    for(q=0;q<4;++q){
                        f[i&1][p][j]+=f[i&1^1][q][j-a[q][p]];
                        if(f[i&1][p][j]>=mod) f[i&1][p][j]%=mod;
                    }
                    //printf("%d %d %d %d
    ",i,j,p,f[i&1][p][j]);
                }
        for(p=0;p<4;++p){
            ans+=f[n&1][p][k];
            if(ans>=mod) ans%=mod;
        }
        printf("%d",ans);
        return 0;
    }

    E. Vasya and Big Integers

    题目大意:给你一个大整数a分为多段,分割的时候分出的每一段的数的范围是l,r(它们也是大整数)问你分割方案数

    解法:同样也是dp,首先设f[i]表示前i个数的划分方案f[i]=sum(f[l]+...+f[r])l,r为这次划分所能取的范围。

    首先使用前缀和可以将求和过程优化为O(1)

    然后就是边界的确定,这个过程是指:整数a在靠后的长度与l的长度相同的一段比较,这会使右边界波动;与r的比较会使左边界波动

    直接比较每次就需要将这两个大整数比较,如果有很多相同的部分就会耗时很多,因此我们需要一个预处理提前算好每一段中最大公共前缀。

    首先另外去一个字符串为a+'*'+l中间的*主要是为了分割和防止过多匹配

    用y[i]表示从i开始与从0开始的最多可以匹配到哪一位

    记录下已经匹配的最靠右区间,然后如果目前i在这个区间就说明至少能匹配到y[i-l]。

    如果不在就从0开始。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=1e6+5,M=998244353;
    char a[N],l[N<<1],r[N<<1];
    int d[N<<1],e[N<<1],s[N],f,tf;
    void cl(char *x,int ll,int *y){
        for(int i=1,l=0,r=0;i<ll;++i){
            if(i<=r) y[i]=min(y[i-l],r-i+1);
            for(;i+y[i]<ll&&x[y[i]]==x[i+y[i]];++y[i]);
            if(r<=i+y[i]-1) l=i,r=i+y[i]-1;
        }
    }
    int main(){
        scanf("%s%s%s",a,l,r);
        int l1=strlen(a),l2=strlen(l),l3=strlen(r);
        l[l2]=r[l3]='*';
        for(int i=0;i<l1;++i) l[l2+i+1]=r[l3+i+1]=a[i];
        cl(l,l1+l2+1,d);
        cl(r,l1+l3+1,e);
        for(int i=0;i<l2;++i) s[i]=1;
        for(int i=l2;i<=l1;++i){
            int ll,rr;
            if(i<l3) ll=-1;
            else if(e[i+1]==l3||r[e[i+1]]>=a[i-l3+e[i+1]]) ll=i-l3-1;
            else ll=i-l3;
            if(d[i+1]==l2||l[d[i+1]]<=a[i-l2+d[i+1]]) rr=i-l2;
            else rr=i-l2-1;
            if(rr==-1||ll>rr);
            else if(ll==-1) f=s[rr];
            else f=(s[rr]-s[ll]+M)%M;
            if(tf&&l[0]=='0'&&l2==1) f=(f+tf)%M,tf=0;
            s[i]=s[i-1];
            if(a[i]=='0') tf=f;
            else s[i]=(s[i]+f)%M;
        }
        printf("%d",f);
        return 0;
    }

    F. The Shortest Statement

    题目大意:给你一个无向联通图,保证边数永远小于点数+20,给你q个询问,每个询问问你两个点间的最短路

    解法:首先如果我们先只把n-1条边加入使其构成数,不考虑通过其他边的情况就可以lca解决

    如果要通过这些边那么至少要通过其中一个点,对于每个边我们都取一个点最多21个点,在包含所有边的图中跑dijstra此时最短距离就是dis[i][u]+dis[i][v];

    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    typedef long long ll;
    const int N=1e5+5;
    struct X{
        int v,f,n,p,q;
    }x[N<<1];
    ll d[N],dis[45][N];
    int fa[N],s,f[N][20],dep[N];
    struct Y{
        int v;
        ll q;
        bool operator<(const Y &a)const{
            return q>a.q;
        }
    };
    priority_queue<Y>dl;
    int fi(int a){
        int b=a,c;
        while(fa[b]) b=fa[b];
        while(a!=b){
            c=fa[a];
            fa[a]=b;
            a=c;
        }
        return a;
    }
    void add(int u,int v,int q,int p){
        x[++s].n=x[u].f;
        x[x[u].f=s].q=q;
        x[s].v=v;
        x[s].p=p;
    }
    void dfs(int u){
        for(int i=1;i<20;++i) f[u][i]=f[f[u][i-1]][i-1];
        for(int i=x[u].f;i;i=x[i].n)
            if(!x[i].p&&x[i].v!=1&&!d[x[i].v]){
                d[x[i].v]=d[u]+x[i].q;
                dep[x[i].v]=dep[u]+1;
                f[x[i].v][0]=u;
                dfs(x[i].v);
            }
    }
    void dij(int u){
        dis[++s][u]=0;
        dl.push((Y){u,0});
        while(dl.size()){
            Y t=dl.top();dl.pop();
            for(int i=x[t.v].f;i;i=x[i].n)
                if(dis[s][x[i].v]>x[i].q+t.q){
                    dis[s][x[i].v]=t.q+x[i].q;
                    dl.push((Y){x[i].v,dis[s][x[i].v]});
                }
        }    
    }
    int main(){
        int n,m,qq;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;++i){
            int u,v,q,sf;
            scanf("%d%d%d",&u,&v,&q);
            sf=fi(u)==fi(v);
            add(u,v,q,sf);
            add(v,u,q,sf);
            if(!sf) fa[fi(v)]=fi(u);
        }
        dfs(1);s=0;
        memset(dis,0x3f,sizeof(dis));
        for(int i=1;i<2*m;i+=2)
            if(x[i].p) dij(x[i].v);
        scanf("%d",&qq);
        while(qq--){
            int u,v;
            scanf("%d%d",&u,&v);
            if(dep[u]<dep[v]) swap(u,v);
            int c=dep[u]-dep[v],tu=u,tv=v;
            ll  ans=d[u]+d[v];
            for(int i=0;c;c>>=1,++i)
                if(c&1) u=f[u][i];
            if(u!=v){
                for(int i=19;i>=0;--i)
                    if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
                u=f[u][0];
            }
            ans-=d[u]*2;
            for(int i=1;i<=s;++i)
                if(ans>dis[i][tu]+dis[i][tv]) ans=dis[i][tu]+dis[i][tv];
            printf("%I64d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    使用递归实现字符串的反转
    .NetCore利用BlockingCollection实现简易消息队列
    .Net Core WebApi控制器接收原始请求正文内容
    反思
    重新解读DDD领域驱动设计(一)
    《实现领域驱动设计》笔记(1)-开卷有益总览
    我来悟微服务(3)-需求管理
    我来悟微服务(2)-惊魂一刻
    Bing.com在.NET Core 2.1上运行!
    Window下mysql环境配置问题整理
  • 原文地址:https://www.cnblogs.com/bzmd/p/9713194.html
Copyright © 2020-2023  润新知