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\n",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\n",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\n",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\n");
    for(long long i=l;i<=r;++i){
        printf("%I64d ",i);
        if((i-l)%2) printf("\n");
    }
    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\n");
        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\n",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\n",ans);
    }
    return 0;
}