AtCoder Grand Contest 031题解

时间:2019-03-18
本文章向大家介绍AtCoder Grand Contest 031题解,主要包括AtCoder Grand Contest 031题解使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题面

传送门

题解

比赛的之后做完\(AB\)就开始发呆了……简直菜的一笔啊……

\(A - Colorful\ Subsequence\)

如果第\(i\)个字母选,那么它前面任意一个别的字母的选择方法为\(cnt_x+1\)种,其中\(cnt_x\)为出现次数,直接乱搞就行了

 //minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
int read(char *s){
    R int len=0;R char ch;while(((ch=getc())>'z'||ch<'a'));
    for(s[++len]=ch;(ch=getc())>='a'&&ch<='z';s[++len]=ch);
    return s[len+1]='\0',len;
}
const int N=1e5+5,P=1e9+7;
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
char s[N];int a[N],cnt[31],flag,n,res,tmp;
int main(){
//  freopen("testdata.in","r",stdin);
    n=read(),read(s);
    fp(i,0,25)cnt[i]=1;
    for(R int i=1;i<=n;++i){
        tmp=1;
        fp(j,0,25)if(j+'a'!=s[i])tmp=mul(tmp,cnt[j]);
        res=add(res,tmp),++cnt[s[i]-'a'];
    }
    printf("%lld\n",res);
    return 0;
}

\(B - Reversi\)

颜色覆盖我们可以看做选择一个区间,那么选择的区间不能有交。易知两个最终序列不同就是选择的区间不同,直接\(dp\)就行了

 //minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=2e5+5,P=1e9+7;
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int c[N],sum[N],g[N],n,res,tot;
int main(){
//  freopen("testdata.in","r",stdin);
    n=read();
    fp(i,1,n)c[i]=read();
    for(R int i=1;i<=n;++i)(c[i]!=c[i-1])?c[++tot]=c[i]:0;
    g[0]=1;
    fp(i,1,tot){
        g[i]=add(sum[c[i]],g[i-1]),
        sum[c[i]]=add(sum[c[i]],g[i-1]);
    }
    printf("%d\n",g[tot]);
    return 0;
}

\(C - Differ\ by\ 1\ Bit\)

很神仙的构造题

首先一次转移相当于对这个数的二进制某一位取反。那么显然\(a,b\)的二进制位上\(1\)的个数的奇偶性必定不同,否则肯定无解。然后我们接下来证明如果\(a,b\)的二进制位上\(1\)的个数奇偶性相同必有解

考虑归纳证明,首先\(n=1\)时显然成立(因为这个时候显然是\(a=1,b=0\)\(a=0,b=1\)

然后设当\(n=1,...k-1\)的时候都成立,接下来我们要证当\(n=k\)的时候成立

因为\(a,b\)的二进制不同的位数为奇数,那么我们随便选取一个不同的位设为第\(x\)位,然后选取一个数\(c\)满足去掉第\(x\)位之后\(c\)\(a\)的二进制\(1\)的个数的奇偶性不同(比方说把\(a\)的第一位取反就行了),根据归纳法,去掉第\(x\)位的情况下,我们可以构造出一个长度为\(2^{n-1}\)的序列,以\(a\)开头以\(c\)结尾,我们强制这前半部分的第\(x\)位和\(a\)相同,对于后半部分也类似构造,并强制它们第\(x\)位和\(b\)相同。易证这个方案必然成立

//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R int x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]=' ';
}
int n,a,b;
void write(int a,int b,int lim){
    if(a==b)return print(a),void();
    int x=(a^b)&(-(a^b));
    lim^=x;
    int y=lim&-lim;
    write(a,a^y,lim),write(a^y^x,b,lim);
}
int main(){
//  freopen("testdata.in","r",stdin);
    scanf("%d%d%d",&n,&a,&b);
    if(__builtin_popcount(a^b)&1^1)return puts("NO"),0;
    puts("YES");
    write(a,b,(1<<n)-1);
    return Ot(),0;
}

\(D - A\ Sequence\ of\ Permutations\)

我的抽代简直就是一张白纸你让我做这种题……

首先\(p,q\)可以看做两个置换

\(f(p,q)\)表示的置换中的第\(p_i\)个元素是\(q_i\),也就是\(p_i\to q_i\)

那么让我们康康啊,\(p\)代表\(i\to p_i\)\(q\)代表\(i->q_i\)……

那么\(qp^{-1}\)就代表\(p_i\to i\to q_i\)

所以\(f(p,q)=qp^{-1}\)

然后大力推出前几项

\[a_1=p\\a_2=q\\a_3=qp^{-1}\\a_4=qp^{-1}q^{-1}\\a_5=qp^{-1}q^{-1}pq^{-1}\\a_6=qp^{-1}q^{-1}ppq^{-1}\\a_7=qp^{-1}q^{-1}pqpq^{-1}\\a_8=qp^{-1}q^{-1}pqp^{-1}qpq^{-1}\]

\(A=qp^{-1}q^{-1}p\)据说可以归纳证明得出\(a_n=Aa_{n-6}A^{-1}(n>6)\)

然后就是上置换的快速幂就行了

//minamoto
#include<bits/stdc++.h>
#define R register
#define vec vector<int>
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R int x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]=' ';
}
const int N=1e5+5;
vec p,q,ip,iq,a[9];int n,k;
vec Inv(const vec &A){
    vec B(n);
    fp(i,0,n-1)B[A[i]]=i;
    return B;
}
vec Mul(const vec &A,const vec &B){
    vec C(n);
    fp(i,0,n-1)C[i]=A[B[i]];
    return C;
}
vec ksm(vec x,int y){
    vec res(n);fp(i,0,n-1)res[i]=i;
    for(;y;y>>=1,x=Mul(x,x))if(y&1)res=Mul(res,x);
    return res;
}
int main(){
//  freopen("testdata.in","r",stdin);
    n=read(),k=read(),p.resize(n),q.resize(n);
    fp(i,0,n-1)p[i]=read()-1;
    fp(i,0,n-1)q[i]=read()-1;
    ip=Inv(p),iq=Inv(q),a[1]=p,a[2]=q;
    fp(i,3,6)a[i]=Mul(a[i-1],Inv(a[i-2]));
    int len=(k-1)/6;
    vec cir=ksm(Mul(q,Mul(ip,Mul(iq,p))),len);
    vec res=Mul(cir,Mul(a[k-len*6],Inv(cir)));
    fp(i,0,n-1)print(res[i]+1);
    return Ot(),0;
}

\(E - Snuke\ the\ Phantom\ Thief\)

首先,我们先枚举选的珠宝的个数\(k\)

然后先来考虑一维的情况,对于\(L\ a_i\ b_i\)来说,就是小于等于\(a_i\)的数最多选\(b_i\)个,等价于选的第\(b_i+1\)个的坐标要大于等于\(a_i+1\)

同理,\(R\ a_i\ b_i\)就代表选的第\(k-b_i\)个的坐标要小于等于\(a_i-1\)

那么我们对于每一个位置,都得到了一个区间\([L_i,R_i]\),表示选的第\(i\)个数的横坐标要在这个区间内(显然这里有\(L_i\leq L_{i+1}\),因为如果\(L_i>L_{i+1}\)等价于把\(L_{i+1}\)设为\(Li\),那么同理\(R_i\leq R_{i+1}\)

然后建二分图,左边\(k\)个点,每个点向右边\(n\)个点中的可行点连边,跑一个最大权匹配就行了

然后回来考虑二维的情况,左边\(k\)个点表示\(x\)坐标的限制,中间\(2n\)个点左边表示\(x\)右边表示\(y\)\(xy\)之间连对应的珠宝的边权,右边\(k\)个点表示\(y\)坐标的限制,跑一个费用流就行了

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3f
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
ll read(){
    R ll res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
inline char getop(){R char ch;while((ch=getc())>'Z'||ch<'A');return ch;}
const int N=505;
struct eg{int v,nx,w;ll c;}e[N*N];int head[N],tot;
inline void add(R int u,R int v,R ll c){
    e[++tot]={v,head[u],1,c},head[u]=tot,
    e[++tot]={u,head[v],0,-c},head[v]=tot;
}
int x[N],y[N],a[N],b[N],t[N],LL[N],RR[N],DD[N],UU[N],vis[N],pe[N],n,m,S,T;
ll v[N],dis[N],res,ans;queue<int>q;
bool spfa(){
    memset(dis,0x3f,sizeof(dis));
    dis[S]=0,q.push(S);
    while(!q.empty()){
        int u=q.front();q.pop(),vis[u]=0;
        go(u)if(e[i].w&&(cmin(dis[v],dis[u]+e[i].c)?pe[v]=i,1:0)&&!vis[v])q.push(v),vis[v]=1;
    }
    if(dis[T]==inf)return false;
    res+=dis[T];
    for(R int i=T;i!=S;i=e[pe[i]^1].v)--e[pe[i]].w,++e[pe[i]^1].w;
    return true;
}
ll calc(int k){
    fp(i,1,k)LL[i]=DD[i]=0,RR[i]=UU[i]=19260817;
    fp(i,1,m)if(b[i]<k){
        switch(t[i]){
            case 'L':LL[b[i]+1]=a[i]+1;break;
            case 'R':RR[k-b[i]]=a[i]-1;break;
            case 'D':DD[b[i]+1]=a[i]+1;break;
            case 'U':UU[k-b[i]]=a[i]-1;break;
        }
    }
    fp(i,2,k)cmax(LL[i],LL[i-1]),cmax(DD[i],DD[i-1]);
    fd(i,k-1,1)cmin(RR[i],RR[i+1]),cmin(UU[i],UU[i+1]);
    memset(head,0,sizeof(head)),tot=1;
    S=0,T=(n+k)<<1|1;
    fp(i,1,n)add(i,n+i,-v[i]);
    fp(i,1,k){
        add(S,n+n+i,0),add(n+n+k+i,T,0);
        fp(j,1,n){
            if(x[j]>=LL[i]&&x[j]<=RR[i])add(n+n+i,j,0);
            if(y[j]>=DD[i]&&y[j]<=UU[i])add(n+j,n+n+k+i,0);
        }
    }
    res=0;while(spfa());
    return -res;
}
int main(){
//  freopen("testdata.in","r",stdin);
    n=read();
    fp(i,1,n)x[i]=read(),y[i]=read(),v[i]=read();
    m=read();
    fp(i,1,m)t[i]=getop(),a[i]=read(),b[i]=read();
    fp(i,1,n)cmax(ans,calc(i));
    printf("%lld\n",ans);
    return 0;
}

\(F - Walk\ on\ Graph\)

首先把问题给倒过来考虑:初始时在\(t\),权值为\(0\),每一次走过一条长度为\(c\)的边会使权值变为(\(2x+c)\bmod p\),求是否存在方案使得到达\(s\)时权值为\(r\)

设状态\((x,y)\)表示在\(x\)点权值为\(y\)的情况,那么相当于问我们能否从\((t,0)\)走到\((s,r)\)

然后我们可以发现一些杏子

首先,状态之间的联通性是双向的,也就是说如果在状态之间连边的话,这是个无向图。考虑一条边\((u,v,w)\),状态之间的转移为\((u,x)\to (v,2x+w)\to (u,4x+3w)\to...\),最终一定可以回到\((u,x)\)。证明的话,显然在模意义下每个\(x\)都有唯一对应的\(2x+c\)以及唯一对应的\({x-c\over 2}\),所以走一个长度为偶数的环就能回到原状态了。

其次,如果存在两条边\((u,v,a)\)\((u,w,b)\),那么有状态\((u,x)\to(v,2x+a)\to(v,4x+3a)\),以及\((u,x)\to(w,2x+b)\to(u,4x+3b)\),这就证明了\((u,4x+3a)\)\((u,4x+3b)\)是等价的,即\((u,x+3(a-b))\)等价

那么我们设\(g\)为所有边权的\(\gcd\),那么对于每一个状态\((u,x)\)\((u,x+3g)\)等价,那么就可以令\(P\)等于\(\gcd(P,3g)\)

那么现在每一条边的边权对\(P\)取模之后必然相等,设这个值为\(z\)。如果我们把所有状态的第二维加上\(z\),再把所有的边权全都减去\(v\),那么新的转移就可以看做\((u,x+z)\to (u,2(x+z)+(c-z))\to (u,2x+c+z)\),那么新的状态和原来的状态之间依然满足一一对应关系

发现在新的状态下,每一次转移的时候状态第二维加上的都是\(c\),肯定是\(g\)的倍数。那么我们从\((u,x)\)能走到的每一个状态肯定能表示为\((u,px+qg)\)的形式。其中\(p\)\(2\)的整数次幂。而对于\(q\)来说,因为\(P\mid 3g\),所以\(q\in\{0,1,2\}\)

而且也有\((u,x)\to(v,2x+c)\to(u,4x+3c)=(u,4x)\),所以\(p\in\{1,2\}\),那么可以用\(6n\)个状态来表示所有的状态了

对于询问就相当于询问\((t,z)\)能否到达\((s,r+z)\),那么我们需要找到一组\(p,q\)满足\((t,x)\)\((s,px+qg)\)联通,且\(pz+qg=r+z\),这个预处理一下每个数是否等于某个\(2\)的奇/偶数次幂,然后直接枚举就行了

//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=1e6+5;
int a[N],b[N],c[N],fa[N],ok[2][N];
int n,m,q,g,z,P;
int find(R int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void merge(R int x,R int y){fa[find(y)]=find(x);}
inline int id(R int x,R int y,R int z){return x*6+(y<<1)+z;}
int main(){
//  freopen("testdata.in","r",stdin);
    n=read(),m=read(),q=read(),P=read();
    fp(i,1,m)a[i]=read(),b[i]=read(),c[i]=read(),g=__gcd(g,abs(c[i]-c[1]));
    !g?g=P:0,P=__gcd(P,3*g),z=c[1]%g;
    fp(i,0,n*6-1)fa[i]=i;
    fp(i,1,m){
        int u=a[i]-1,v=b[i]-1,w=(c[i]-z)/g%3;
        fp(x,0,2){
            merge(id(u,x,0),id(v,((x<<1)+w)%3,1)),
            merge(id(v,x,0),id(u,((x<<1)+w)%3,1)),
            merge(id(u,x,1),id(v,((x<<1)+w)%3,0)),
            merge(id(v,x,1),id(u,((x<<1)+w)%3,0));
        }
    }
//  fp(i,0,n*6-1)printf("%d %d\n",i,find(i));
    for(R int i=0,j=z;i<(P<<1);++i,j=(j<<1)%P)ok[i&1][j]=1;
    while(q--){
        int s=read()-1,t=read()-1,r=read(),res=0;
        fp(x,0,2)fp(y,0,1)find(id(t,0,0))==find(id(s,x,y))?res|=ok[y][(r+z+(3-x)*g)%P]:0;
        puts(res?"YES":"NO");
    }
    return 0;
}