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;
    }
    
  • 相关阅读:
    IP地址结构分类(包括主机号和网络好计算)
    抓包工具fiddler的Https证书设置
    获取目录结构,并写到txt文档里
    十五、React:简单点餐实例:知识点,html解析写法
    git commit -m 和 git commit -am 区别
    用Git管理项目进行版本控制
    pycharm 设置项目的编译器
    十四、 React路由(react-router4.x): 动态路由、get传值、React中使用url模块
    十三、react-router 4.x的基本配置
    进程间的通讯
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/10548483.html
Copyright © 2011-2022 走看看