zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest 016题解

    传送门

    (A)

    直接枚举最终的字母然后模拟就行了……

    就这数据范围还是别学我写的这种做法了……

    const int N=105;
    char s[N];int las[26],mx[26],n,res;
    int main(){
    	scanf("%s",s+1),n=strlen(s+1),res=n;
    	fp(i,1,n)s[i]-='a';
    	fp(i,1,n)cmax(mx[s[i]],i-las[s[i]]-1),las[s[i]]=i;
    	fp(i,0,25)cmax(mx[i],n-las[i]),cmin(res,mx[i]);
    	printf("%d
    ",res);
    	return 0;
    }
    

    (B)

    所有颜色可以被分成只出现(1)次的和出现多次的,而前者的(a_i)必定比后者小(1)

    如果所有的(a_i)都相等,考虑到底是全出现(1)次还是全出现多次,否则必定是形如若干个(k-1)(k),记有(t)(k-1),那么判断一下(t)个只出现一次的和(n-t)个出现多次的是否合法就行了

    const int N=1e5+5;
    int a[N],n;
    int main(){
    	scanf("%d",&n);
    	fp(i,1,n)scanf("%d",&a[i]);
    	sort(a+1,a+1+n);
    	if(a[1]<a[n]-1)return puts("No"),0;
    	if(a[1]==a[n])return puts(a[1]==n-1||n>=(a[1]<<1)?"Yes":"No"),0;
    	R int t=1;for(;a[t]==a[t+1];++t);
    	puts(a[n]>t&&n>=t+((a[n]-t)<<1)?"Yes":"No");
    	return 0;
    }
    

    (C)

    最优策略肯定是尽量让所有子矩阵的和为(-1)

    考虑这样一种方法,在每一个(h|i,w|j)((i,j))位置放一个(sk=-(k imes (hw-1)+1)),剩下所有位置都放(k),那么每一个(h imes w)的子矩阵都会包含且仅包含一个(sk)(hw-1)(k),且总和为(-1)

    同时我们还要保证整个矩阵的和为正数,设(a=n/h,b=m/w)(都是下取整),那么([1,1])([ah,bw])的这个子矩阵的和必定是(-ab),而剩下的部分因为都是填(k),所以只有让(k)尽量大才有可能让整个矩阵的和为正数,那么取能取的最大的(k)就行了

    //quming
    #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)
    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;
    typedef long long ll;
    const int N=505,L=1e9;
    int mp[N][N],n,m,h,w,k;ll sum,sk;
    int main(){
    	scanf("%d%d%d%d",&n,&m,&h,&w);
    	if(h==1&&w==1)return puts("No"),0;
    	k=(L-1)/(h*w-1),sum=1ll*k*(n*m-(n/h*h)*(m/w*w));
    	sk=1ll*k*(h*w-1)+1;
    	if((n/h)*(m/w)>sum)return puts("No"),0;
    	puts("Yes");
    	for(R int i=h;i<=n;i+=h)for(R int j=w;j<=m;j+=w)mp[i][j]=1;
    	fp(i,1,n){
    		fp(j,1,m)printf("%d ",mp[i][j]?-sk:k);
    		puts("");
    	}
    	return 0;
    }
    

    (D)

    并没有想到用权值建点……

    首先我们把(a_{n+1})设为所有数的异或和,那么一次操作等价于交换(a_{n+1})和某个数

    对于每一个(i),如果(a_i=b_i)显然不用进行操作,否则我们把它们对应的权值连一条边,这样图里会形成若干个连通块,我们发现每条边都要有一次贡献,而且跨越连通块的时候也会有一次贡献,所以答案就是边数加上联通块个数减一

    不过有可能一开始最后一个元素并没有边连出去,那么他要进入某个连通块才能继续,所以这种情况下答案要(+1)

    //quming
    #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)
    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;
    const int N=2e5+5;
    int a[N],b[N],c[N],d[N],fa[N],n,tot,res;map<int,int>mp;
    inline int find(R int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    inline int ID(R int x){return mp[x]?mp[x]:mp[x]=++tot;}
    int main(){
    	scanf("%d",&n);
    	fp(i,1,n)scanf("%d",&a[i]),a[n+1]^=a[i];
    	fp(i,1,n)scanf("%d",&b[i]),b[n+1]^=b[i];
    	fp(i,1,n+1)c[i]=a[i],d[i]=b[i];
    	sort(c+1,c+2+n),sort(d+1,d+2+n);
    	fp(i,1,n+1)if(c[i]!=d[i])return puts("-1"),0;
    	fp(i,1,n+1)fa[i]=i;
    	fp(i,1,n)if(a[i]!=b[i]){
    		R int u=ID(a[i]),v=ID(b[i]);
    		fa[find(u)]=find(v),++res;
    	}
    //	puts("qwq");
    	if(!res)return puts("0"),0;
    	fp(i,1,tot)if(find(i)==i)++res;
    	bool fl=1;
    	fp(i,1,n)if(b[i]==a[n+1])fl=0;
    	printf("%d
    ",res+fl-1);
    	return 0;
    }
    

    (E)

    好秒的题目……一开始口胡了一个(O(nmlog n))的做法结果发现题目看错了……

    我们先考虑一个更一般的问题,即定义(f_{S,r})表示在(r)步之后集合(S)中的元素是否可以全部存在,是的话记为(1)否则为(0)

    假设第(r)步要吃的鸡是(u,v),那么分情况讨论

    • 如果(uin S)(vin S),则(f_{S,r}=0)

    • 如果(uin S)(v otin S),则(f_{S,r}=f_{Scup{v},r-1})

    • 如果(vin S)(u otin S),同上

    • 否则(f_{S,r}=1)

    对于每一个点(u),初始时令(S={u}),然后我们倒着考虑,不断往(S)中加数,如果有一次不合法就不合法,最终可以判断(u)是否有可能不被吃

    然后是结论,(u,v)可以同时存在当且仅当(u,v)都有可能不被吃且(S_ucap S_v=varnothing)

    前一个条件显然,对于后面那个条件,假设存在一个点(w),满足(win S_u)(win S_v),那么如果(w)在两个集合中用来救了两只不同的鸡,显然(u,v)不可能同时活着了。如果(w)用来救了同一只鸡,那么我们删掉(w),此时必定存在另一个点(p)也同时在两个集合中,继续递归考虑就行了。鉴于最后两个集合分别是({u})({v}),所以一定会有终止的时候

    复杂度为(O(nm+n^3))

    //quming
    #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)
    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;
    const int N=1e5+5;
    bool ins[405][405],vis[405];
    int fr[N],to[N],n,m,res;
    int main(){
    	scanf("%d%d",&n,&m);
    	fp(i,1,m)scanf("%d%d",&fr[i],&to[i]);
    	fp(i,1,n){
    		ins[i][i]=1;
    		fd(j,m,1){
    			R int u=fr[j],v=to[j];
    			if(ins[i][u]&&ins[i][v]){vis[i]=1;break;}
    			if(ins[i][u]||ins[i][v])ins[i][u]=ins[i][v]=1;
    		}
    	}
    	fp(i,1,n)if(!vis[i])fp(j,i+1,n)if(!vis[j]){
    		R int fl=1;
    		fp(k,1,n)if(ins[i][k]&&ins[j][k]){fl=0;break;}
    		res+=fl;
    	}
    	printf("%d
    ",res);
    	return 0;
    }
    

    (F)

    首先先手必赢当且仅当(1,2)点的(SG)值异或和不为(0),因为这个东西很难算,我们考虑计算(1,2)(SG)值异或为(0)的方案数

    对于(U),我们设(f[U])表示点集(U)内部使得(1,2)(SG)值相等的连边方案数,计算的话,我们枚举(S),令(T=U-S),假设(S)中所有数的(SG)值为(0)(即必败态),(T)中所有数的(SG)值不为(0)

    首先(S)内部肯定不能连边(必败态不能转移到必败态),而(T)中每一个点至少得有一条到(S)中的边(必胜态至少有一个后继是必败态),(S)中的任何一个点到(T)随便连边(必败态的后继全是是必胜态),最后连边方案乘上(f[T])就是对应的贡献了

    注意,(1,2)必须得同时在(S)(T)中否则不合法

    为什么这样转移是对的呢?因为我们的(dp)等价于把(U)分成了若干层,其中每一层的(SG)值都是一样的,而我们转移的时候可以保证(1,2)在同一层,所以就没有问题了

    //quming
    #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)
    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;
    const int P=1e9+7;
    inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
    inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
    inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
    inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
    int ksm(R int x,R int y){
    	R int res=1;
    	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    	return res;
    }
    const int N=25,M=(1<<15)+5;
    int to[N],sz[M],lb[M],f[M],bin[M],n,m,lim;
    int main(){
    	scanf("%d%d",&n,&m),lim=(1<<n);
    	for(R int i=1,u,v;i<=m;++i)scanf("%d%d",&u,&v),--u,--v,to[u]|=1<<v;
    	bin[0]=1;fp(i,1,m)bin[i]=mul(bin[i-1],2);
    	fp(i,1,lim-1)sz[i]=sz[i>>1]+(i&1),lb[i]=i&1?0:lb[i>>1]+1;
    	f[0]=1;
    	fp(u,1,lim-1)if((u&1)==(u>>1&1))
    		for(R int s=u;s;s=(s-1)&u)if((s&1)==(s>>1&1)){
    			R int t=u^s,res=f[t];
    			for(R int i=s,j=lb[i];i;i-=i&-i,j=lb[i])
    				res=mul(res,bin[sz[to[j]&t]]);
    			for(R int i=t,j=lb[i];i;i-=i&-i,j=lb[i])
    				res=mul(res,bin[sz[to[j]&s]]-1);
    			upd(f[u],res);
    		}
    	printf("%d
    ",dec(bin[m],f[lim-1]));
    	return 0;
    }
    
  • 相关阅读:
    牛客练习赛51 D题
    Educational Codeforces Round 72 (Rated for Div. 2) C题
    Codeforces Round #583 (Div. 1 + Div. 2, based on Olympiad of Metropolises) C题
    Codeforces Round #583 (Div. 1 + Div. 2, based on Olympiad of Metropolises) A题
    Codeforces Round #583 (Div. 1 + Div. 2, based on Olympiad of Metropolises) A题
    Educational Codeforces Round 72 (Rated for Div. 2) B题
    Educational Codeforces Round 72 (Rated for Div. 2) A题
    《DSP using MATLAB》Problem 7.2
    《DSP using MATLAB》Problem 7.1
    《DSP using MATLAB》Problem 6.24
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11573074.html
Copyright © 2011-2022 走看看