• 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]='',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
    ",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
    ",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 o q_i)

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

    那么(qp^{-1})就代表(p_i o i o 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_ileq L_{i+1}),因为如果(L_i>L_{i+1})等价于把(L_{i+1})设为(Li),那么同理(R_ileq 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
    ",ans);
    	return 0;
    }
    
    

    (F - Walk on Graph)

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

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

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

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

    其次,如果存在两条边((u,v,a))((u,w,b)),那么有状态((u,x) o(v,2x+a) o(v,4x+3a)),以及((u,x) o(w,2x+b) o(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) o (u,2(x+z)+(c-z)) o (u,2x+c+z)),那么新的状态和原来的状态之间依然满足一一对应关系

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

    而且也有((u,x) o(v,2x+c) o(u,4x+3c)=(u,4x)),所以(pin{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
    ",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;
    }
    
  • 相关阅读:
    焦虑来回走
    去省政府客串
    中国地质大学(北京)招生信息有点坑
    张桂梅校长再获殊荣,实至名归!她的故事值得一看再看……
    行内容转为列内容
    公文写作心得
    钟南山院士亲口说的“如何保持健康长寿
    VMware虚拟机出现“内部错误”如何解决?
    CI框架深入篇(2)一些基础的我之不知道的标准格式
    SQL语句学习记录(三)
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/10554416.html
Copyright © 2020-2023  润新知