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