zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest 031 简要题解

    AtCoder Grand Contest 031

    Atcoder

    A - Colorful Subsequence

    description

    (s)中本质不同子序列的个数模(10^9+7)。两个子序列不同当且仅当存在一种字符在两者中的出现次数不同。

    (|s|le10^5)

    solution

    (prod_{i='a'}^{'z'}(mbox{字符}imbox{出现的次数}+1)-1)

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')w=0,ch=getchar();
    	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N=1e5+5;
    const int mod=1e9+7;
    int n,t[26],ans=1;char s[N];
    int main(){
    	scanf("%d",&n);scanf("%s",s+1);
    	for(int i=1;i<=n;++i)++t[s[i]-'a'];
    	for(int i=0;i<26;++i)ans=1ll*ans*(t[i]+1)%mod;
    	printf("%d
    ",(ans+mod-1)%mod);return 0;
    }
    

    B - Reversi

    description

    有一个长为(n)的序列,每个位置有个颜色,可以进行若干次操作,操作为选择两个颜色相同的位置,将它们中间的位置全部变成这个颜色,求所有可能出现的颜色序列的方案数模(10^9+7)

    (n,c_ile2 imes10^5)

    solution

    (f_i=f_{i-1}+[lst_i<i-1]f_{lst_i})

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')w=0,ch=getchar();
    	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N=2e5+5;
    const int mod=1e9+7;
    int n,f[N],lst[N];
    int main(){
    	n=gi();f[0]=1;
    	for(int i=1;i<=n;++i){
    		int a=gi();
    		f[i]=f[i-1];
    		if(lst[a]&&lst[a]<i-1)f[i]=(f[i]+f[lst[a]])%mod;
    		lst[a]=i;
    	}
    	printf("%d
    ",f[n]);return 0;
    }
    

    C - Differ by 1 Bit

    description

    构造一个(0)(2^n-1)的排列,满足首位是(A)末位是(B),且相邻两个数在二进制下仅相差(1)位。

    (nle 17)

    solution

    只需要构造首位是(0)末位是(A mbox{xor} B)的排列再将所有数异或(A)就行了。

    显然,(mbox{popcount}(Ambox{xor}B))为偶数时一定无解,否则根据构造方法一定存在一组解。

    考虑将模型转化为在(n)维超立方体上的游走,需要遍历这个超立方体(2^n)个顶点中的每一个。一种可行的策略是,先遍历(n)维超立方体中的某个(n-1)维超立方体,再一步走到另一个(n-1)维超立方体上并遍历之。

    在本题中我们要从(0)走到(Ambox{xor}B),可以选出一个二进制位(p)满足(Ambox{xor}B)在第(p)位上值为(1),遍历二进制第(p)位为(0)的所有点组成的(n-1)维超立方体后,再去遍历二进制位第(p)位为(1)的所有点组成的(n-1)维超立方体。两个(n-1)维超立方体之间的连接点的选取是任意的,只需要满足起点终点二进制位不同的位数是奇数就行了。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')w=0,ch=getchar();
    	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    #define pc(x) __builtin_popcount(x)
    int n,A,B,all;
    void dfs(int x,int y,int ban){
    	if(pc(ban^all)==1){printf("%d %d ",y,x^y);return;}
    	for(int i=0;i<n;++i)
    		if((~ban>>i&1)&&(x>>i&1))
    			for(int j=0;j<n;++j)
    				if((~ban>>j&1)&&i!=j){
    					dfs(1<<j,y,ban|1<<i);
    					dfs(x^(1<<i)^(1<<j),y^(1<<i)^(1<<j),ban|1<<i);
    					return;
    				}
    }
    int main(){
    	n=gi(),A=gi(),B=gi(),all=(1<<n)-1;
    	if(pc(A^B)&1)puts("YES"),dfs(A^B,A,0);
    	else puts("NO");
    	return 0;
    }
    

    D - A Sequence of Permutations

    description

    定义(f(p,q))为一个排列(c)满足(c_{p_i}=q_i),其中(p,q)均是(1-n)排列。已知(a_1=p,a_2=q,a_n=f(a_{n-2},a_{n-1})(n>2)),给出(p,q),求(a_k)

    (nle10^5,kle10^9)

    solution

    ([f(p,q)]_{p_i}=q_i o f(p,q)p=q o f(p,q)=qp^{-1})

    然后大力列出(a_i)的前若干项。

    [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))

    所以就做完啦?

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')w=0,ch=getchar();
    	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    #define vi vector<int>
    int n,k;vi a[7];
    vi readin(){
    	vi res(n);
    	for(int i=0;i<n;++i)res[i]=gi()-1;
    	return res;
    }
    void print(vi a){
    	for(int i=0;i<n;++i)printf("%d ",a[i]+1);
    	puts("");
    }
    vi inv(vi a){
    	vi res(n);
    	for(int i=0;i<n;++i)res[a[i]]=i;
    	return res;
    }
    vi mul(vi a,vi b){
    	vi res(n);
    	for(int i=0;i<n;++i)res[i]=a[b[i]];
    	return res;
    }
    vi fastpow(vi a,int b){
    	vi res(n);
    	for(int i=0;i<n;++i)res[i]=i;
    	while(b){if(b&1)res=mul(res,a);a=mul(a,a);b>>=1;}
    	return res;
    }
    int main(){
    	n=gi();k=gi();
    	vi p=readin(),q=readin();
    	a[1]=p;a[2]=q;
    	for(int i=3;i<=6;++i)a[i]=mul(a[i-1],inv(a[i-2]));
    	vi A=mul(mul(q,inv(p)),mul(inv(q),p)),B=fastpow(A,(k-1)/6);
    	print(mul(mul(B,a[(k-1)%6+1]),inv(B)));return 0;
    }
    

    E - Snuke the Phantom Thief

    description

    二维平面上有(n)个珠宝,第(i)个珠宝的位置为((x_i,y_i)),权值为(v_i)。你可以从中选取任意数量的珠宝,但需要满足如下(m)条限制:所有(横/纵)坐标(小/大)于等于(a_i)的珠宝中至多选(b_i)个。求能得到的最大权值和。

    (n le80,mle 320,x_i,y_i,a_ile100,v_ile10^{15},b_i<n)

    solution

    首先这数据范围看着就很费用流

    先考虑一维怎么做。

    一个很妙的转化是:限制横坐标(le a_i)的珠宝里至多选(b_i)个,等价于选择的横坐标第(b_i+1)小的珠宝,其横坐标必须(> a_i)

    如果是限制横坐标(ge a_i)的珠宝至多选(b_i)个,则可以先枚举选(k)个珠宝,然后限制就等价于选择的第(k-b_i)个珠宝其横坐标必须(<a_i)。(以上只考虑(b_i<k)的限制,(b_ige k)的限制显然无效)

    这样我们就可以得到(k)个二元组((l_j,r_j)),分别表示第(j)个珠宝的横坐标的范围限制。注意这(k)个二元组的(l)(r)应满足单调不降。

    这样我们就得到了一个匹配的模型:二分图一侧有(k)个点,另一侧有(n)个点,满足范围限制的点之间连边,然后求一组最大权匹配即可。

    至于二维的问题,可以直接把图拆成三份,即左侧(k)个点表示横坐标的限制,中间(2n)个点内部连权值的边表示珠宝,右侧另(k)个点表示纵坐标的限制。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    using namespace std;
    #define ll long long
    ll gi(){
    	ll x=0,w=1;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')w=0,ch=getchar();
    	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    int gc(){
    	char ch=getchar();
    	while(ch<'A'||ch>'Z')ch=getchar();
    	return ch;
    }
    const int N=405;
    struct edge{int to,nxt,w;ll cost;}E[N*N];
    int n,m,x[N],y[N],t[N],a[N],b[N],L[N],R[N],D[N],U[N],head[N],cnt,S,T,vis[N],pe[N];
    ll v[N],dis[N],ans;queue<int>Q;
    void link(int u,int v,ll w){
    	E[++cnt]=(edge){v,head[u],1,w};head[u]=cnt;
    	E[++cnt]=(edge){u,head[v],0,-w};head[v]=cnt;
    }
    bool spfa(ll &res){
    	memset(dis,63,sizeof(dis));
    	dis[S]=0;Q.push(S);
    	while(!Q.empty()){
    		int u=Q.front();Q.pop();vis[u]=0;
    		for(int i=head[u],v;i;i=E[i].nxt)
    			if(E[i].w&&dis[v=E[i].to]>dis[u]+E[i].cost){
    				dis[v]=dis[u]+E[i].cost;pe[v]=i;
    				if(!vis[v])vis[v]=1,Q.push(v);
    			}
    	}
    	if(dis[T]==dis[0])return false;res+=dis[T];
    	for(int i=T;i!=S;i=E[pe[i]^1].to)--E[pe[i]].w,++E[pe[i]^1].w;
    	return true;
    }
    ll cal(int k){
    	for(int i=1;i<=k;++i)L[i]=D[i]=0,R[i]=U[i]=233;
    	for(int i=1;i<=m;++i)
    		if(b[i]<k){
    			if(t[i]=='L')L[b[i]+1]=a[i]+1;
    			if(t[i]=='R')R[k-b[i]]=a[i]-1;
    			if(t[i]=='D')D[b[i]+1]=a[i]+1;
    			if(t[i]=='U')U[k-b[i]]=a[i]-1;
    		}
    	for(int i=2;i<=k;++i)L[i]=max(L[i],L[i-1]),D[i]=max(D[i],D[i-1]);
    	for(int i=k-1;i;--i)R[i]=min(R[i],R[i+1]),U[i]=min(U[i],U[i+1]);
    	memset(head,0,sizeof(head));cnt=1;S=n+k<<1|1;T=n+k+1<<1;
    	for(int i=1;i<=n;++i)link(i,n+i,-v[i]-1000000000000000ll);
    	for(int i=1;i<=k;++i){
    		link(S,n+n+i,0);link(n+n+k+i,T,0);
    		for(int j=1;j<=n;++j){
    			if(L[i]<=x[j]&&x[j]<=R[i])link(n+n+i,j,0);
    			if(D[i]<=y[j]&&y[j]<=U[i])link(n+j,n+n+k+i,0);
    		}
    	}
    	ll res=0;while(spfa(res));return -res-1000000000000000ll*k;
    }
    int main(){
    	n=gi();
    	for(int i=1;i<=n;++i)x[i]=gi(),y[i]=gi(),v[i]=gi();
    	m=gi();
    	for(int i=1;i<=m;++i)t[i]=gc(),a[i]=gi(),b[i]=gi();
    	for(int i=1;i<=n;++i)ans=max(ans,cal(i));
    	printf("%lld
    ",ans);return 0;
    }
    

    F - Walk on Graph

    description

    有一张(n)(m)边无向图和一个奇数(mod),边有边权,给出(q)组询问,每次询问是否存在一条从(s)走到(t)的长度对(mod)取模后等于(r)的路径。路径不要求是简单路径,即可以来回走。这里路径长度的定义并非所有边的长度之和,而是经过的第(i)条边的长度乘上(2^i)之和。

    (n,m,q le5 imes10^4,3le modle 10^6,2 mid mod)

    solution

    把路径反过来,问题转化成了:

    一个人初始在(t)点,有一个数(x)初值为(0)。每当他走过一条长度为(c)的边,数(x)就会变成(2x+c)并对(mod)取模,问是否存在一条路径使走到(s)点后数(x)恰好等于(r)

    这样暴力的做法就是建立(n imes mod)个状态然后连边,询问就是查询状态((t,0))是否可以到达状态((s,r))

    通过观察可以发现一些性质:

    1、所有的边(指状态之间的连边)都是双向的。考虑一条边((a,b,c)),通过这条边可以实现((a,x) o(b,2x+c) o(a,4x+3c) o(b,8x+7c) o...),而最终一定可以回到状态((a,x))证明略显然在模意义下每个(x)都有唯一对应的(2x+c)以及唯一对应的(frac{x-c}{2}),所以走一个长度为偶数的环就能回到原状态了。

    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))与状态((u,x+3(a-b)))是等价的。

    由此,我们可以求出所有有公共点的边的边权之差的(gcd)(由于图是连通的,所以这等价于任意两条边边权之差的(gcd)),设之为(g),则对于所有点而言,状态((u,x))与状态((u,x+3g))均是等价的。那么我们就可以令(mod gets gcd(mod,3g))了。

    观察到此时每条边的边权(mod g)都是相等的,不妨设为(z)。可以考虑通过一些奇技淫巧把(z)去掉:把所有边权减去(z),同时把所有状态的第二维抬高(z),即原本的((u,x) o(v,2x+c))变成((u,x+z) o(v,2(x+z-z)+(c-z)+z)=(v,2x+c))。这样转化后所有转移都可以与转化前一一对应,因此正确性不存在问题,而经过这样的转化后,我们可以发现每次转移后非(x)项加上的常数都是(g)的倍数。因此,从一个状态((u,x))出发能够到达的所有状态,一定都可以被表示成((v,px+qg))的形式,其中(p)(2)的非负整数次幂,(q)是任意非负整数,进一步的,因为(mod|3g),故(qin{0,1,2})

    而因为((u,x) o(v,2x+c) o(u,4x+3c)=(u,4x)),所以(pin{1,2})。于是,我们便可以设计总共(6n)个状态表示((u,px+qg)),其中(uin[1,n],pin{1,2},qin{0,1,2})

    这样询问时就需要询问((t,z))能否到达((s,r+z))。相当于我们需要找到一组((p,q),pin N^*,qin{0,1,2}),满足((t,x))((s,(pmod 2)x+qg))连通且(pz+qg=r+z)。所以可以预处理出([1,mod))中每个数是否等于某个(2)的奇数/偶数次幂,然后对于每组询问直接枚举((p,q))即可。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')w=0,ch=getchar();
    	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N=1e6+5;
    int n,m,q,mod,a[N],b[N],c[N],g,z,fa[N],chk[2][N];
    int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
    void link(int x,int y){fa[find(x)]=find(y);}
    int id(int u,int x,int y){return u*6+x*2+y;}
    int main(){
    	n=gi();m=gi();q=gi();mod=gi();
    	for(int i=1;i<=m;++i){
    		a[i]=gi(),b[i]=gi(),c[i]=gi();
    		g=__gcd(g,abs(c[i]-c[1]));
    	}
    	if(!g)g=mod;mod=__gcd(mod,3*g);z=c[1]%g;
    	for(int i=0;i<n*6;++i)fa[i]=i;
    	for(int i=1;i<=m;++i){
    		int u=a[i]-1,v=b[i]-1,w=(c[i]-z)/g%3;
    		for(int x=0;x<3;++x){
    			link(id(u,x,0),id(v,(2*x+w)%3,1));
    			link(id(u,x,1),id(v,(2*x+w)%3,0));
    			link(id(v,x,0),id(u,(2*x+w)%3,1));
    			link(id(v,x,1),id(u,(2*x+w)%3,0));
    		}
    	}
    	for(int i=0,j=z;i<mod<<1;++i,j=(j<<1)%mod)chk[i&1][j]=1;
    	while(q--){
    		int s=gi()-1,t=gi()-1,r=gi(),res=0;
    		for(int x=0;x<3;++x)
    			for(int y=0;y<2;++y)
    				if(find(id(t,0,0))==find(id(s,x,y)))
    					res|=chk[y][(r+z+(3-x)*g)%mod];
    		puts(res?"YES":"NO");
    	}
    	return 0;
    }
    
  • 相关阅读:
    48. Rotate Image
    83. Remove Duplicates from Sorted List
    46. Permutations
    HTML5笔记
    18. 4Sum
    24. Swap Nodes in Pairs
    42. Trapping Rain Water
    Python modf() 函数
    Python min() 函数
    Python max() 函数
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/10548483.html
Copyright © 2011-2022 走看看