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]='\0',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\n",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\n",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\to q_i\)
那么让我们康康啊,\(p\)代表\(i\to p_i\),\(q\)代表\(i->q_i\)……
那么\(qp^{-1}\)就代表\(p_i\to i\to q_i\)了
所以\(f(p,q)=qp^{-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_i\leq L_{i+1}\),因为如果\(L_i>L_{i+1}\)等价于把\(L_{i+1}\)设为\(Li\),那么同理\(R_i\leq 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\n",ans);
return 0;
}
\(F - Walk\ on\ Graph\)
首先把问题给倒过来考虑:初始时在\(t\),权值为\(0\),每一次走过一条长度为\(c\)的边会使权值变为(\(2x+c)\bmod p\),求是否存在方案使得到达\(s\)时权值为\(r\)
设状态\((x,y)\)表示在\(x\)点权值为\(y\)的情况,那么相当于问我们能否从\((t,0)\)走到\((s,r)\)
然后我们可以发现一些杏子
首先,状态之间的联通性是双向的,也就是说如果在状态之间连边的话,这是个无向图。考虑一条边\((u,v,w)\),状态之间的转移为\((u,x)\to (v,2x+w)\to (u,4x+3w)\to...\),最终一定可以回到\((u,x)\)。证明的话,显然在模意义下每个\(x\)都有唯一对应的\(2x+c\)以及唯一对应的\({x-c\over 2}\),所以走一个长度为偶数的环就能回到原状态了。
其次,如果存在两条边\((u,v,a)\)和\((u,w,b)\),那么有状态\((u,x)\to(v,2x+a)\to(v,4x+3a)\),以及\((u,x)\to(w,2x+b)\to(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)\to (u,2(x+z)+(c-z))\to (u,2x+c+z)\),那么新的状态和原来的状态之间依然满足一一对应关系
发现在新的状态下,每一次转移的时候状态第二维加上的都是\(c\),肯定是\(g\)的倍数。那么我们从\((u,x)\)能走到的每一个状态肯定能表示为\((u,px+qg)\)的形式。其中\(p\)是\(2\)的整数次幂。而对于\(q\)来说,因为\(P\mid 3g\),所以\(q\in\{0,1,2\}\)
而且也有\((u,x)\to(v,2x+c)\to(u,4x+3c)=(u,4x)\),所以\(p\in\{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\n",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;
}