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;
    }
    
  • 相关阅读:
    EAX、ECX、EDX、EBX寄存器的作用
    MFC VS2005 添加Override 和 Message
    ActiveX添加测试工程, 出现的问题[非选择性参数][找不到成员]
    两种应该掌握的排序方法--------2.quick Sort
    关于I/O的那点事
    整理一下 编码、解码库
    VC一些经验系列: 《分享泄漏检测工具:内存、DC、GDI、Handle... 》
    golang安装卸载 linux+windows+raspberryPI 平台
    (转)如何正确使用C++多重继承
    单播、多播(也称组播)、广播
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11573074.html
Copyright © 2011-2022 走看看