模拟题大集合(3)

被yspm骂了鸽了好几场模拟赛于是决定补一下。
这场非常不嘚,\(T1\)考WQS没学过,\(T2\)考场上冲一发LCT结果T成\(shit\)了,\(T3\)还考个冷门循环矩阵行列式,不知道有啥意义。

星际广播

基础\(DP\)为设\(f_{i,j}\)表示前i个位置用了\(j\)次覆盖操作的最小代价,转移考虑\(f_{i,j}\to f_{i+l,j+1}/f_{i+1,j}\)
然后发现\(ans_i\)表示用了\(i\)次操作的答案具有凸性,于是可以WQS二分,去掉第二维之后转移时带上操作次数即可。
另外这题用结构体暴T不止,换成pair之后就√了,不知道为啥

Code


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("radio.in","r",stdin);freopen("radio.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=1e6+10;const db eps=1e-6;
	std::pair<db,int>f[N];int n,m,l,ans=N,pre[N];char s[N];
	inline bool check(db mid){
		memset(f,0,sizeof(f));
		for(int i=1;i<=n;i++)f[i]=max(f[i-1],{f[max(0,i-l)].first+pre[i]-pre[max(0,i-l)]-mid,f[max(0,i-l)].second+1});
		return f[n].second<=m;
	}
	inline void solve(char ch){
		F(i,1,n)pre[i]=pre[i-1]+(s[i]!=ch);
		db l=0,r=n,an=0;
		while(r-l>eps){
			db mid=(l+r)/2;
			if(check(mid))r=mid,an=mid;
			else l=mid;
		}check(an);
		ans=min(ans,pre[n]-round(f[n].first+m*an));
	}
	inline short main(){
		file();
		n=read(),m=read(),l=read();
		scanf("%s",s+1);
		solve('Y'),solve('R'),solve('B');
		pi(ans);
		return 0;
	}
}
signed main(){return EMT::main();}

星际航道

考虑平面图和对偶图的关系,平面图的最小生成树的补集就是对偶图的最大生成树,证明显然
于是LCT维护一下两个生成树上的边即可。

Code


#include<vector>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("channel.in","r",stdin);freopen("channel.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
	int R,C,m,tp;
	namespace XII{
		const int N=3e5+10,inf=0x3f3f3f3f;
		inline int id(int x,int y){return (x<1||y<1)?0:(x-1)*C+y;}ll lans;
		inline void decode(char ch, int &x, int &y, int &w) {
			static int mask = 0xfffff;
			w = (int) ((w ^ lans) & mask);
			if(ch == '-') x = (x + lans - 1) % R + 1, y = (y + lans - 1) % (C - 1) + 1;
			if(ch == '|') x = (x + lans - 1) % (R - 1) + 1, y = (y + lans - 1) % C + 1;
		}
		struct LCT1{
			int son[N][2],mxid[N],mx[N],val[N],fa[N],lz[N];
			inline void up(int x){
				mx[x]=val[x],mxid[x]=x;
				if(son[x][0]){
					if(mx[x]<mx[son[x][0]])
						mx[x]=mx[son[x][0]],mxid[x]=mxid[son[x][0]];
				}
				if(son[x][1]){
					if(mx[x]<mx[son[x][1]])
						mx[x]=mx[son[x][1]],mxid[x]=mxid[son[x][1]];
				}
			}
			inline bool nroot(int x){return son[fa[x]][0]==x||son[fa[x]][1]==x;}
			inline void res(int x){
				int y=fa[x],z=fa[y],k=son[y][1]==x,w=son[x][!k];
				if(nroot(y))son[z][son[z][1]==y]=x;
				fa[x]=z,fa[y]=x;son[x][!k]=y,son[y][k]=w;if(w)fa[w]=y;up(y);
			}
			inline void swap(int &x,int &y){x^=y^=x^=y;}
			inline void rev(int x){
				swap(son[x][0],son[x][1]),lz[x]^=1;
			}
			inline void down(int x){
				if(lz[x])rev(son[x][0]),rev(son[x][1]),lz[x]=0;
			}
			inline void splay(int x){
				static int s[N];
				int y=x,z=0;s[++z]=y;
				while(nroot(y))s[++z]=(y=fa[y]);
				while(z)down(s[z--]);
				while(nroot(x)){
					y=fa[x],z=fa[y];
					if(nroot(y))res((son[y][1]==x)^(son[z][1]==y)?x:y);
					res(x);up(x),up(y);
				}
			}
			inline void access(int x){
				for(int y=0;x;x=fa[y=x])
					splay(x),son[x][1]=y,up(x);
			}
			inline int findroot(int x){
				access(x),splay(x);
				while(son[x][0])down(x),x=son[x][0];
				splay(x);return x;
			}
			inline void makeroot(int x){
				access(x),splay(x),rev(x);
			}
			inline void split(int x,int y){
				makeroot(x),access(y),splay(y);
			}
			inline void link(int x,int y){
				makeroot(x),fa[x]=y;
			}
			inline void cut(int x,int y){
				split(x,y);son[y][0]=fa[x]=0;up(y);
			}
		}t1;
		struct LCT2{
			int son[N][2],mnid[N],mn[N],val[N],fa[N],lz[N];
			inline void up(int x){
				mn[x]=val[x],mnid[x]=x;
				if(son[x][0]){
					if(mn[x]>mn[son[x][0]])
						mn[x]=mn[son[x][0]],mnid[x]=mnid[son[x][0]];
				}
				if(son[x][1]){
					if(mn[x]>mn[son[x][1]])
						mn[x]=mn[son[x][1]],mnid[x]=mnid[son[x][1]];
				}
			}
			inline bool nroot(int x){return son[fa[x]][0]==x||son[fa[x]][1]==x;}
			inline void res(int x){
				int y=fa[x],z=fa[y],k=son[y][1]==x,w=son[x][!k];
				if(nroot(y))son[z][son[z][1]==y]=x;
				fa[x]=z,fa[y]=x;son[x][!k]=y,son[y][k]=w;if(w)fa[w]=y;up(y);
			}
			inline void swap(int &x,int &y){x^=y^=x^=y;}
			inline void rev(int x){
				swap(son[x][0],son[x][1]),lz[x]^=1;
			}
			inline void down(int x){
				if(lz[x])rev(son[x][0]),rev(son[x][1]),lz[x]=0;
			}
			inline void splay(int x){
				static int s[N];
				int y=x,z=0;s[++z]=y;
				while(nroot(y))s[++z]=(y=fa[y]);
				while(z)down(s[z--]);
				while(nroot(x)){
					y=fa[x],z=fa[y];
					if(nroot(y))res((son[y][1]==x)^(son[z][1]==y)?x:y);
					res(x);up(x),up(y);
				}
			}
			inline void access(int x){
				for(int y=0;x;x=fa[y=x])
					splay(x),son[x][1]=y,up(x);
			}
			inline int findroot(int x){
				access(x),splay(x);
				while(son[x][0])down(x),x=son[x][0];
				splay(x);return x;
			}
			inline void makeroot(int x){
				access(x),splay(x),rev(x);
			}
			inline void split(int x,int y){
				makeroot(x),access(y),splay(y);
			}
			inline void link(int x,int y){
				makeroot(x),fa[x]=y;
			}
			inline void cut(int x,int y){
				split(x,y);son[y][0]=fa[x]=0;up(y);
			}
		}t2;
		int cnt,e1[N],e2[N],w[N],fr1[N],to1[N],fr2[N],to2[N],col[N],wawdawd[N<<1],icnt,fa[N];int *id2;
		inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
		inline void solve(){
			id2=wawdawd+R+C;
			F(i,1,R-1){
				F(j,1,C-1){
					id2[id(i,j)]=++icnt;
				}
			}++icnt;F(i,1,icnt)t2.val[i]=inf,t2.mn[i]=inf,t2.mnid[i]=i;
			F(i,0,R)F(j,1,C)if(!i||i==R||!j||j==C)id2[id(i,j)]=icnt;id2[0]=icnt;
			cnt=id(R,C);
			F(i,1,R)F(j,1,C)fa[id(i,j)]=id(i,j);
			F(i,1,R){
				F(j,1,C){
					if(j<C){
						e1[id(i,j)]=++cnt;
						fr1[cnt]=id(i,j),
						to1[cnt]=id(i,j+1);
						fr2[cnt]=id2[id(i-1,j)];
						to2[cnt]=id2[id(i,j)];
						if(find(id(i,j))!=find(id(i,j+1))){
							fa[find(id(i,j))]=find(id(i,j+1));col[cnt]=0;
							t1.link(fr1[cnt],cnt),t1.link(cnt,to1[cnt]);
						}else{
							col[cnt]=1;
							t2.link(fr2[cnt],cnt),t2.link(cnt,to2[cnt]);
						}
					}
					if(i<R){
						e2[id(i,j)]=++cnt;
						fr1[cnt]=id(i,j);
						to1[cnt]=id(i+1,j);
						fr2[cnt]=id2[id(i,j-1)];
						to2[cnt]=id2[id(i,j)];
						if(find(id(i,j))!=find(id(i+1,j))){
							fa[find(id(i,j))]=find(id(i+1,j));col[cnt]=0;
							t1.link(fr1[cnt],cnt),t1.link(cnt,to1[cnt]);
						}else{
							col[cnt]=1;
							t2.link(fr2[cnt],cnt),t2.link(cnt,to2[cnt]);
						}
					}
				}
			}
			while(m--){
				char ch=getchar();while(ch!='-'&&ch!='|')ch=getchar();
				int x=read(),y=read(),z=read();
				if(tp)decode(ch,x,y,z);
				int ID=ch=='-'?e1[id(x,y)]:e2[id(x,y)];
				if(col[ID]){
					t1.makeroot(ID);t1.val[ID]=z;t1.up(ID);
					t2.makeroot(ID);t2.val[ID]=z;t2.up(ID);
					t1.split(fr1[ID],to1[ID]);
					if(t1.mx[to1[ID]]>z){
						int mid=t1.mxid[to1[ID]];
						lans-=t1.mx[to1[ID]],lans+=z;
						t1.cut(fr1[mid],mid),t1.cut(mid,to1[mid]);
						t2.cut(fr2[ID],ID),t2.cut(ID,to2[ID]);
						col[ID]^=1,col[mid]^=1;
						t1.link(fr1[ID],ID),t1.link(ID,to1[ID]);
						t2.link(fr2[mid],mid),t2.link(mid,to2[mid]);
					}
				}else{
					t1.makeroot(ID),lans-=t1.val[ID],t1.val[ID]=z,lans+=z,t1.up(ID);
					t2.makeroot(ID);t2.val[ID]=z;t2.up(ID);
					t2.split(fr2[ID],to2[ID]);
					if(t2.mn[to2[ID]]<z){
						int mid=t2.mnid[to2[ID]];
						lans-=z,lans+=t2.mn[to2[ID]];
						t1.cut(fr1[ID],ID),t1.cut(ID,to1[ID]);
						t2.cut(fr2[mid],mid),t2.cut(mid,to2[mid]);
						col[ID]^=1,col[mid]^=1;
						t1.link(fr1[mid],mid),t1.link(mid,to1[mid]);
						t2.link(fr2[ID],ID),t2.link(ID,to2[ID]);
					}
				}pi(lans);pn();
			}
		}
	}
	inline short main(){
		file();
		tp=read();
		R=read(),C=read(),m=read();
		XII::solve();
		return 0;
	}
}
signed main(){return EMT::main();}

星际联邦

暴力就是矩阵树定理求行列式,由于本题要求的是内向树森林所以还要建一个虚点,然后暴力高斯消元可以拿到40pts高分。
正解就是发现这个矩阵是个循环矩阵,那么使用NTT求出来的点值乘积就是行列式,证明可以考虑乘上一个范德蒙德矩阵().

Code


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("federation.in","r",stdin);freopen("federation.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=1.1e6,mod=998244353,g=3,gi=(mod+1)/3;
	int n,a[N],r[N],lim,l;
	inline int ksm(int a,int b){
		int ans=1;
		while(b){
			if(b&1)ans=1ll*a*ans%mod;
			a=1ll*a*a%mod;b>>=1;
		}return ans;
	}
	inline short main(){
		file();
		l=read();lim=n=1<<l;
		for(int i=1;i<n;i++)a[i]=read();
		a[0]=1;
		for(int i=1;i<n;i++)a[0]=(a[0]+a[i])%mod;
		for(int i=1;i<n;i++)a[i]=mod-a[i];
		std::reverse(a+1,a+n);
		for(int i=0;i<lim;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
		for(int i=0;i<lim;i++)if(i<r[i])std::swap(a[i],a[r[i]]);
		for(int mid=1;mid<lim;mid<<=1){
			int wn=ksm(g,(mod-1)/(mid<<1));
			for(int j=0;j<lim;j+=(mid<<1)){
				int w=1;
				for(int k=0;k<mid;k++,w=1ll*w*wn%mod){
					int x=a[j+k],y=1ll*w*a[j+mid+k]%mod;
					a[j+k]=(x+y)%mod;
					a[j+mid+k]=(x-y+mod)%mod;
				}
			}
		}int ans=1;
		for(int i=0;i<n;i++)ans=1ll*ans*a[i]%mod;
		pi(ans);
		return 0;
	}
}
signed main(){return EMT::main();}

淦,T3注释没删丢了30pts,可惜

Max

\(f_{i,S,k}\)为前\(i\)个点用掉的操作集合是\(S\)并且最大值为\(k\)的概率,转移的话考虑处理出来\(g_{i+1,S,k}\)表示\(i\)点用了操作集合为\(S\)后值为\(k\)的概率,状压dp即可。

Code
#include<cassert>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("max.in","r",stdin);freopen("max.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int mod=1e9+7;
	int f[42][1<<10][32],g[12][1<<10][32],pre[1<<10][32],n,m,c,p[12][42][5],count[1<<10];
	inline int M(int x){return x>=mod?x-mod:x;}
	inline void add(int &x,int v){x+=v,x-=x>=mod?mod:0;}
	inline void DP(int x){
		memset(g,0,sizeof(g));
		g[0][0][0]=1;
		F(j,1,m){
			for(int s=0;s<(1<<j);s++)
				for(int k=0;k<=(j-1)*c;k++)if(g[j-1][s][k]){
					for(int w=0;w<=c;w++)
						add(g[j][s|(1<<(j-1))][k+w],1ll*g[j-1][s][k]*p[j][x][w]%mod);
					add(g[j][s][k],g[j-1][s][k]);
				}
		}
		for(int s=0;s<(1<<m);s++){
			pre[s][0]=g[m][s][0];
			F(k,1,count[s]*c){
				pre[s][k]=M(pre[s][k-1]+g[m][s][k]);
			}
		}
	}
	inline short main(){
		file();
		n=read(),m=read(),c=read();
		F(i,1,m)F(j,1,n)F(k,0,c)p[i][j][k]=read();
		f[0][0][0]=1;int all=(1<<m)-1;
		for(int i=1;i<(1<<m);i++)count[i]=count[i>>1]+(i&1);
		for(int i=0;i<n;i++){
			DP(i+1);
			for(int j=0;j<(1<<m);j++)for(int k=0;k<=c*count[j];k++)if(f[i][j][k]){
				add(f[i+1][j][k],1ll*f[i][j][k]*g[m][0][0]%mod);
				for(int t=(all^j);t;t=(t-1)&(all^j)){
					add(f[i+1][j|t][k],1ll*f[i][j][k]*pre[t][min(k,count[t]*c)]%mod);
					for(int w=k+1;w<=c*count[t];w++)if(g[m][t][w])
						add(f[i+1][j|t][w],1ll*f[i][j][k]*g[m][t][w]%mod);
				}
			}
		}int ans=0;
		F(i,0,c*m)add(ans,1ll*f[n][all][i]*i%mod);
		pi(ans);
		return 0;
	}
}
signed main(){return EMT::main();}

Paint

注意到答案下界是\(2(max\{h,w\}+1)\),于是答案矩形一定会过\(y=\frac{h}{2}\)或者\(x=\frac{w}{2}\),那么线段树维护一下到每个点的答案,然后维护一下分界线两边的单调栈即可。

Code
#include<cassert>
#include<map>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("paint.in","r",stdin);freopen("paint.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=2e5+10;
	int w,h,n,ans,topl,topr;
	struct pt{int x,y;}p[N],sl[N],sr[N];
	struct seg{
		int val[N<<2],lz[N<<2];
		inline void init(){memset(val,0,sizeof(int)*(n<<2));memset(lz,0,sizeof(int)*(n<<2));}
		inline void add(int p,int v){val[p]+=v;lz[p]+=v;}
		inline void down(int p){if(lz[p]){add(p<<1,lz[p]),add(p<<1|1,lz[p]),lz[p]=0;}}
		inline void change(int p,int l,int r,int ql,int qr,int v){
			if(l>=ql&&r<=qr){add(p,v);return;}
			int mid=(l+r)>>1;down(p);
			if(qr<=mid)change(p<<1,l,mid,ql,qr,v);
			else if(ql>mid)change(p<<1|1,mid+1,r,ql,qr,v);
			else change(p<<1,l,mid,ql,mid,v),change(p<<1|1,mid+1,r,mid+1,qr,v);
			val[p]=max(val[p<<1],val[p<<1|1]);
		}
	}segm;
	std::map<int,int>mp[2];
	/*
	1 6
	4 1
	6 9
	9 4
	*/
	inline void solvex(){
		std::sort(p+1,p+n+1,[](pt a,pt b){return a.x<b.x;});
		F(i,1,n){
			if(i-1&&p[i].x==p[i-1].x){
				if(p[i].y<=h/2){
					while(topl&&sl[topl].y<=p[i].y){
						if(max(sl[topl-1].x,1)<=sl[topl].x-1)segm.change(1,1,n,sl[topl-1].x,sl[topl].x-1,sl[topl].y-p[i].y);
						topl--;
					}sl[++topl]={i,p[i].y};
				}else{
					while(topr&&sr[topr].y>=p[i].y){
						if(max(sr[topr-1].x,1)<=sr[topr].x-1)segm.change(1,1,n,sr[topr-1].x,sr[topr].x-1,p[i].y-sr[topr].y);
						topr--;
					}sr[++topr]={i,p[i].y};
				}
				continue;
			}
			if(i>1)segm.change(1,1,n,i-1,i-1,h),segm.change(1,1,n,1,i-1,p[i].x-p[i-1].x);
			ans=max(ans,segm.val[1]);
			if(p[i].y<=h/2){
				if(i-1)segm.change(1,1,n,max(sl[topl].x,1),i-1,-p[i].y);
				while(topl&&sl[topl].y<=p[i].y){
					if(max(sl[topl-1].x,1)<=sl[topl].x-1)segm.change(1,1,n,max(sl[topl-1].x,1),sl[topl].x-1,sl[topl].y-p[i].y);
					topl--;
				}sl[++topl]={i,p[i].y};
			}else{
				if(i-1)segm.change(1,1,n,max(sr[topr].x,1),i-1,-(h-p[i].y));
				while(topr&&sr[topr].y>=p[i].y){
					if(max(sr[topr-1].x,1)<=sr[topr].x-1)segm.change(1,1,n,max(sr[topr-1].x,1),sr[topr].x-1,p[i].y-sr[topr].y);
					topr--;
				}sr[++topr]={i,p[i].y};
			}
		}
	}
	inline void solvey(){
		std::sort(p+1,p+n+1,[](pt a,pt b){return a.y<b.y;});topl=topr=0;segm.init();
		F(i,1,n){
			if(i-1&&p[i].y==p[i-1].y){
				if(p[i].x<=w/2){
					while(topl&&sl[topl].x<=p[i].x){
						if(max(sl[topl-1].y,1)<=sl[topl].y-1)segm.change(1,1,n,max(sl[topl-1].y,1),sl[topl].y-1,sl[topl].x-p[i].x);
						topl--;
					}sl[++topl]={p[i].x,i};
				}else{
					while(topr&&sr[topr].x>=p[i].x){
						if(max(sr[topr-1].y,1)<=sr[topr].y-1)segm.change(1,1,n,max(sr[topr-1].y,1),sr[topr].y-1,p[i].x-sr[topr].x);
						topr--;
					}sr[++topr]={p[i].x,i};
				}
				continue;
			}
			if(i>1)segm.change(1,1,n,i-1,i-1,w),segm.change(1,1,n,1,i-1,p[i].y-p[i-1].y);
			ans=max(ans,segm.val[1]);
			if(p[i].x<=w/2){
				if(i-1)segm.change(1,1,n,max(sl[topl].y,1),i-1,-p[i].x);
				while(topl&&sl[topl].x<=p[i].x){
					if(max(sl[topl-1].y,1)<=sl[topl].y-1)segm.change(1,1,n,max(sl[topl-1].y,1),sl[topl].y-1,sl[topl].x-p[i].x);
					topl--;
				}sl[++topl]={p[i].x,i};
			}else{
				if(i-1)segm.change(1,1,n,max(sr[topr].y,1),i-1,-(w-p[i].x));
				while(topr&&sr[topr].x>=p[i].x){
					if(max(sr[topr-1].y,1)<=sr[topr].y-1)segm.change(1,1,n,max(sr[topr-1].y,1),sr[topr].y-1,p[i].x-sr[topr].x);
					topr--;
				}sr[++topr]={p[i].x,i};
			}
		}
	}
	inline short main(){
		file();
		w=read(),h=read(),n=read();
		F(i,1,n){
			p[i].x=read(),p[i].y=read();
			assert(mp[0].find(p[i].x)==mp[0].end());
			assert(mp[1].find(p[i].y)==mp[1].end());
			mp[0][p[i].x]=1,mp[1][p[i].y]=1;
		}
		p[++n]={0,0},p[++n]={w,h};
		solvex();solvey();
		pi(ans<<1);
		return 0;
	}
}
signed main(){return EMT::main();}

Decompose

好像是动态DP裸题...然而不会动态DP的我考场上只能写链的部分分了...结果还把前几个包的暴力注释了,淦。
考虑转换矩阵乘法定义,乘法改成加法,加法改成取\(\max\)难发现这样的矩阵依然具有结合率,于是修改的时候维护一下矩阵即可。
数组开小了调了一个钟头,淦

Code
#include<set>
#include<vector>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("decompose.in","r",stdin);freopen("decompose.out","w",stdout);}
	inline ll max(ll a,ll b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
	const int N=1e5+10;const ll inf=0x3f3f3f3f3f3f3f3f;
	int n,m,L,ti,top[N],end[N],siz[N],dfn[N],time[N],son[N],fa[N],w[N][5],deep[N];
	std::vector<int>g[N];std::multiset<ll>mxc[N][3];ll sumc[N];
	struct mar1{
		ll a[4][4];
		mar1(){memset(a,-0x3f,sizeof(a));}
		friend mar1 operator *(mar1 a,mar1 b){
			mar1 c;
			for(int k=0;k<L;k++)
				for(int i=0;i<L;i++)
					for(int j=0;j<L;j++)
						c.a[i][k]=max(c.a[i][k],a.a[i][j]+b.a[j][k]);
			return c;
		}
	};
	struct mar2{
		ll a[4];
		mar2(){memset(a,-0x3f,sizeof(a));}
		friend mar2 operator *(mar2 a,mar1 b){
			mar2 c;
			for(int i=0;i<L;i++)
				for(int j=0;j<L;j++)
					c.a[i]=max(c.a[i],a.a[j]+b.a[j][i]);
			return c;
		}
		inline ll mx(){ll ans=-inf;for(int i=0;i<L;i++)ans=max(ans,a[i]);return ans;}
	};
	inline bool leaf(int x){return end[top[x]]==x;}
	struct seg{
		mar1 t[N<<2];
		inline void up(int p){t[p]=t[p<<1|1]*t[p<<1];}
		inline void build(int p,int l,int r){
			if(l==r){
				if(leaf(time[l]))return;
				int x=time[l];
				F(i,1,L)F(j,1,L){
					if(i+1==j)t[p].a[i-1][j-1]=w[x][j]+max(*mxc[x][i-1].rbegin(),0)+sumc[x];
					else if(j!=1)t[p].a[i-1][j-1]=w[x][j]+*mxc[x][j-2].rbegin()+sumc[x];
					else t[p].a[i-1][j-1]=w[x][1]+sumc[x];
				}return;
			}int mid=(l+r)>>1;
			build(p<<1,l,mid),build(p<<1|1,mid+1,r);
			up(p);
		}
		inline mar1 ask(int p,int l,int r,int ql,int qr){
			if(l>=ql&&r<=qr)return t[p];
			int mid=(l+r)>>1;
			if(qr<=mid)return ask(p<<1,l,mid,ql,qr);
			if(ql>mid)return ask(p<<1|1,mid+1,r,ql,qr);
			return ask(p<<1|1,mid+1,r,mid+1,qr)*ask(p<<1,l,mid,ql,mid);
		}
		inline void change(int p,int l,int r,int x){
			if(l==r){
				if(leaf(time[l]))return;
				int x=time[l];
				F(i,1,L)F(j,1,L){
					if(i+1==j)t[p].a[i-1][j-1]=w[x][j]+max(*mxc[x][i-1].rbegin(),0)+sumc[x];
					else if(j!=1)t[p].a[i-1][j-1]=w[x][j]+*mxc[x][j-2].rbegin()+sumc[x];
					else t[p].a[i-1][j-1]=w[x][1]+sumc[x];
				}return;
			}
			int mid=(l+r)>>1;
			if(x<=mid)change(p<<1,l,mid,x);
			else change(p<<1|1,mid+1,r,x);
			up(p);
		}
	}segm;
	inline void dfs1(int x){
		siz[x]=1;
		for(auto y:g[x]){
			deep[y]=deep[x]+1,dfs1(y),siz[x]+=siz[y];
			if(siz[son[x]]<siz[y])son[x]=y;
		}
	}
	inline void dfs2(int x,int tp){
		top[x]=tp,time[dfn[x]=++ti]=x,end[top[x]]=x;
		if(!son[x])return;
		dfs2(son[x],tp);
		for(auto y:g[x])if(y!=son[x])
			dfs2(y,y);
	}
	namespace DP{
		ll f[N][5],mx[N];
		inline void upd(int x){
			memset(f[x],-0x3f,sizeof(f[x]));mx[x]=-inf;
			ll sum=0;for(auto &y:g[x])sum+=mx[y];f[x][1]=w[x][1]+sum;
			for(auto &y:g[x])F(j,1,L-1)f[x][j+1]=max(f[x][j+1],sum-mx[y]+f[y][j]+w[x][j+1]);
			F(i,1,L)mx[x]=max(mx[x],f[x][i]);
		}
		inline void dfs(int x){for(auto &y:g[x])dfs(y);upd(x);}
		inline void init(){
			dfs(1);
			F(i,1,n)if(son[fa[i]]!=i){
				F(j,1,L-1)mxc[fa[i]][j-1].insert(f[i][j]-mx[i]);
				sumc[fa[i]]+=mx[i];
			}
			F(i,1,n)F(j,1,L-1)mxc[i][j-1].insert(-inf);
			segm.build(1,1,n);
		}
	}
	inline mar2 ask(int x){
		mar2 ans;int ed=end[top[x]];ans.a[0]=w[ed][1];
		if(x!=ed)ans=ans*segm.ask(1,1,n,dfn[x],dfn[ed]-1);
		return ans;
	}
	inline void del(int x){
		mar2 w;
		while(x){
			x=top[x];w=ask(x);
			if(fa[x]){
				F(i,1,L-1)mxc[fa[x]][i-1].erase(mxc[fa[x]][i-1].find(w.a[i-1]-w.mx()));
				sumc[fa[x]]-=w.mx();
			}x=fa[x];
		}
	}
	inline void change(int x){
		mar2 w;
		segm.change(1,1,n,dfn[x]);
		while(x){
			x=top[x];w=ask(x);
			if(fa[x]){
				F(i,1,L-1)mxc[fa[x]][i-1].insert(w.a[i-1]-w.mx());
				sumc[fa[x]]+=w.mx();
				segm.change(1,1,n,dfn[fa[x]]);
			}x=fa[x];
		}
	}
	inline short main(){
		file();
		n=read(),m=read(),L=read();
		F(i,2,n)g[fa[i]=read()].push_back(i);
		F(i,1,n)F(j,1,L)w[i][j]=read();
		dfs1(1);dfs2(1,1);DP::init();
		while(m--){
			int x=read();del(x);
			F(i,1,L)w[x][i]=read();
			change(x);
			pi(ask(1).mx());pn();
		}
		return 0;
	}
}
signed main(){return EMT::main();}

首先有暴力 \(dp\) :
\(f_{i,j,k}\)表示左边到了第i个点,右边现在有j个点k个链的方案数。
转移的话就考虑点和点贡献是\(1\),点和链贡献是\(2\),链和链贡献是\(4\)
那么我们现在考虑把后两维揉在一起\(f_{i,j}\)表示左边到了第i个点,右边点和链有\(j\)个的方案数的\(2^j\)倍,这样的话无论怎么连转移都是一样的了。
那么每次考虑加进去一个点:\(f_{i,j}\to f_{i,j+1}\)
然后考虑用当前点把链练成环:\(f_{i,1}-a_{i}\to ans\)
减去\(a_i\)是因为不能出现二元环。
然后考虑将点、链进行拼接:\(f_{i,j}\times \binom{j}{2} \to f_{i,j-1}\)
这样就考虑完了。

Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("ring.in","r",stdin);freopen("ring.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=5010,mod=998244353;
	int n,a[N],jc[N],inv[N],f[N];
	inline int ksm(int a,int b){int ans=1;while(b){if(b&1)ans=1ll*a*ans%mod;a=1ll*a*a%mod;b>>=1;}return ans;}
	inline int C(int n,int m){return 1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;}
	inline void add(int &x,int v){x+=v,x-=x>=mod?mod:0;}
	inline int M(int x){return x>=mod?x-mod:x;}
	inline short main(){
		file();
		n=read();
		f[0]=1;
		F(i,1,n)a[i]=read();std::sort(a+1,a+n+1);
		int ans=0;
		F(i,1,n){
			for(int j=a[i-1]+1;j<=a[i];j++)
				for(int k=j;k>=1;k--)
					add(f[k],f[k-1]);
			add(ans,M(f[1]-a[i]+mod));
			for(int j=1;j<=a[i];j++)
				add(f[j-1],1ll*j*(j-1)%mod*f[j]%mod);
		}
		pi(1ll*ans*((mod+1)>>1)%mod);
		return 0;
	}
}
signed main(){return EMT::main();}

不会证明
设公差为\(x\),则推得\(k=(a+x)(3x-a)\)
则满足条件的k有且仅有一个拆分\(w_1,w_2\)使得\(k=w_1w_2,x=\frac{w_1+w_2}{4},a=\frac{3w_1-w_2}{4}\)
也就是说\(w_1+w_2\equiv 0 (\bmod\ 4)\)\(3w_1>w_2\)
\(k=u2^v\),发现只有\(v=0,v=2,v=4\)时合法并且分配方式唯一。
\(v=0\)时,则\(w_1,w_2\)都是奇数,那么一定是\(1,3\),另外要保证\(k\)分解唯一,也就是\(k\in p\ \wedge k\equiv 3 (mod\ 4)\)
否则是质数的4倍、16倍。
于是只需要用\(min_25\)筛的\(dp\)思想算出来\(n\)以内\(k\in p\ \wedge k\equiv 3 (mod\ 4)\)\(k\)的个数以及\(\frac{n}{4},\frac{n}{16}\)以内质数个数即可。

Code


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("number.in","r",stdin);freopen("number.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
	const int N=1e6+10;
	int sq;ll n,ans;bool vis[N];int prime[N],co;
	inline void init(int n){
		co=0;memset(vis,0,sizeof(vis));
		F(i,2,n){
			if(!vis[i])prime[++co]=i;
			F(j,1,co){
				if(i*prime[j]>n)break;
				vis[i*prime[j]]=1;
				if(i%prime[j]==0)break;
			}
		}
	}
	inline ll clac(ll n){
		static ll w[N],id1[N],id2[N],f[N];
		w[0]=0;sq=sqrt(n);init(sq);
		memset(f,0,sizeof(f));
		for(ll l=1,r;l<=n;l=r+1){
			r=n/(n/l);w[++w[0]]=n/l;f[w[0]]=w[w[0]]-1;
			if(n/l<=sq)id1[n/l]=w[0];else id2[r]=w[0];
		}
		F(i,1,co){
			F(j,1,w[0]){
				if(1ll*prime[i]*prime[i]>w[j])break;
				int k=w[j]/prime[i]<=sq?id1[w[j]/prime[i]]:id2[n/(w[j]/prime[i])];
				f[j]-=f[k]-(i-1);
			}
		}return f[1];
	}
	int pre[N][2];
	inline void init2(int n){
		co=0;memset(vis,0,sizeof(vis));
		F(i,2,n){
			if(!vis[i]){prime[++co]=i;pre[co][0]=pre[co-1][0]+(i%4==1),pre[co][1]=pre[co-1][1]+(i%4==3);}
			F(j,1,co){
				if(i*prime[j]>n)break;
				vis[i*prime[j]]=1;
				if(i%prime[j]==0)break;
			}
		}
	}
	inline ll clac2(ll n){
		static ll w[N],id1[N],id2[N],f[N][2];
		sq=sqrt(n);init2(sq);
		for(ll l=1,r;l<=n;l=r+1){
			r=n/(n/l);w[++w[0]]=n/l;
			f[w[0]][0]=(w[w[0]]-1)/4;
			f[w[0]][1]=(w[w[0]]+1)/4;
			if(n/l<=sq)id1[n/l]=w[0];else id2[r]=w[0];
		}
		F(i,2,co){
			F(j,1,w[0]){
				if(1ll*prime[i]*prime[i]>w[j])break;
				int k=w[j]/prime[i]<=sq?id1[w[j]/prime[i]]:id2[n/(w[j]/prime[i])];
				if(prime[i]%4==1){
					f[j][0]-=f[k][0]-pre[i-1][0];
					f[j][1]-=f[k][1]-pre[i-1][1];
				}else{
					f[j][0]-=f[k][1]-pre[i-1][1];
					f[j][1]-=f[k][0]-pre[i-1][0];
				}
			}
		}return f[1][1];
	}
	inline short main(){
		file();
		n=read();
		pi(clac(n/4)+clac(n/16)+clac2(n));
		return 0;
	}
}
signed main(){return EMT::main();}

矩阵

\(x_1=1\)的部分分启发我们进行区间历史最值维护。(考试时胡了一下竟然对了)
考虑对\(x\)进行分治,每次找出跨过当前区间\(mid\)的询问,分成\([x1,mid],[mid+1,x2]\)两部分去回答答案,回答完之后就可以丢掉这个询问了。
考虑修改操作如何处理。我们依然进行分治,如果一个操作的\(x\)区间已经覆盖了当前区间,就可以让它挂在这个区间上,否则去下传到左右儿子区间中继续造成贡献。容易看出这样的话一个操作会被操作\(\log_{n}\)次。
再考虑撤销,真实值部分撤销直接改就行了,可是历史最值不能这么做。
一种方法是存下来当前区间对哪些点的值进行了修改,回溯时改回去即可,不过比较废空间。
另一种比较妙。每次全局加上一个极大值\(C\)使得当前的历史最值一定是\(C\)加上当前修改值,然后询问时再把\(C\)减去,这样的话就相当于清除了之前的影响。

Code




#include<cassert>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	#define int long long
	inline ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("matrix.in","r",stdin);freopen("matrix.out","w",stdout);}
	inline ll max(ll a,ll b){return a>b?a:b;}inline ll min(ll a,ll b){return a<b?a:b;}
	inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
	const int N=5e4+10;
	int n,m,Q;ll nowC;struct dpw{int x1,y1,x2,y2;ll v;}qsw[N<<2],cgw[N<<2];struct dp{int l,r;ll v;};
	struct seg{
		ll v1[N<<2],v2[N<<2],lz[N<<2],mxlz[N<<2];
		inline void down(int p){
			if(mxlz[p]){
				v2[p<<1]=max(v2[p<<1],v1[p<<1]+mxlz[p]);
				v2[p<<1|1]=max(v2[p<<1|1],v1[p<<1|1]+mxlz[p]);
				mxlz[p<<1]=max(mxlz[p<<1],lz[p<<1]+mxlz[p]);
				mxlz[p<<1|1]=max(mxlz[p<<1|1],lz[p<<1|1]+mxlz[p]);
				mxlz[p]=0;
			}
			if(lz[p]){
				v1[p<<1]+=lz[p],lz[p<<1]+=lz[p],
				v1[p<<1|1]+=lz[p],lz[p<<1|1]+=lz[p];
				lz[p]=0;
			}
		}
		inline void change(int p,int l,int r,int ql,int qr,ll v){
			if(l>=ql&&r<=qr){
				v1[p]+=v,lz[p]+=v,v2[p]=max(v2[p],v1[p]),mxlz[p]=max(mxlz[p],lz[p]);
				return;
			}int mid=(l+r)>>1;down(p);
			if(qr<=mid)change(p<<1,l,mid,ql,qr,v);
			else if(ql>mid)change(p<<1|1,mid+1,r,ql,qr,v);
			else change(p<<1,l,mid,ql,mid,v),change(p<<1|1,mid+1,r,mid+1,qr,v);
			v1[p]=max(v1[p<<1],v1[p<<1|1]);v2[p]=max(v2[p<<1],v2[p<<1|1]);
		}
		inline ll ask(int p,int l,int r,int ql,int qr){
			if(l>=ql&&r<=qr)return v2[p];
			int mid=(l+r)>>1;down(p);
			if(qr<=mid)return ask(p<<1,l,mid,ql,qr);
			if(ql>mid)return ask(p<<1|1,mid+1,r,ql,qr);
			return max(ask(p<<1,l,mid,ql,mid),ask(p<<1|1,mid+1,r,mid+1,qr));
		}
	}segm;
	ll ans[N<<2];std::vector<dp>cg[N],qs[N];
	inline void solve(int l,int r,std::vector<dpw> &qq,std::vector<dpw> &cc){
		if(l==r){
			nowC=segm.v2[1]+1;
			segm.change(1,1,n,1,n,nowC);
			for(auto &w:cc)segm.change(1,1,n,w.y1,w.y2,w.v);
			for(auto &w:qq)ans[w.v]=max(ans[w.v],segm.ask(1,1,n,w.y1,w.y2)-nowC);
			for(auto &w:cc)segm.change(1,1,n,w.y1,w.y2,-w.v);
			segm.change(1,1,n,1,n,-nowC);
			qq.clear();qq.shrink_to_fit();
			cc.clear();cc.shrink_to_fit();
			return;
		}
		std::vector<dpw>cl,cr,ql,qr,del;
		int mid=(l+r)>>1;
		F(i,l-1,r+1)cg[i].clear(),qs[i].clear();
		for(auto &w:qq)if(w.x2<=mid)ql.push_back(w);
		else if(w.x1>mid)qr.push_back(w);
		else {qs[w.x1].push_back({w.y1,w.y2,w.v});qs[w.x2].push_back({w.y1,w.y2,w.v});}
		for(auto &w:cc)if(w.x1<=l&&w.x2>=r){
			del.push_back(w);
		}else if(w.x2<=mid){
			cl.push_back(w);
			cg[w.x2].push_back({w.y1,w.y2,w.v});
			cg[w.x1-1].push_back({w.y1,w.y2,-w.v});
		}else if(w.x1>mid){
			cr.push_back(w);
			cg[w.x1].push_back({w.y1,w.y2,w.v});
			cg[w.x2+1].push_back({w.y1,w.y2,-w.v});
		}else{
			cl.push_back({w.x1,w.y1,mid,w.y2,w.v});
			cr.push_back({mid+1,w.y1,w.x2,w.y2,w.v});
			cg[mid].push_back({w.y1,w.y2,w.v});
			cg[w.x1-1].push_back({w.y1,w.y2,-w.v});
			cg[mid+1].push_back({w.y1,w.y2,w.v});
			cg[w.x2+1].push_back({w.y1,w.y2,-w.v});
		}
		for(auto &v:del)segm.change(1,1,n,v.y1,v.y2,v.v);
		{
			nowC=segm.v2[1]+1;
			segm.change(1,1,n,1,n,nowC);
			D(i,mid,l-1){
				for(auto w:cg[i])if(w.v<0)segm.change(1,1,n,w.l,w.r,w.v);
				for(auto w:cg[i])if(w.v>=0)segm.change(1,1,n,w.l,w.r,w.v);
				for(auto w:qs[i])ans[w.v]=max(ans[w.v],segm.ask(1,1,n,w.l,w.r)-nowC);
			}
			segm.change(1,1,n,1,n,-nowC);
		}//solvel
		{
			nowC=segm.v2[1]+1;
			segm.change(1,1,n,1,n,nowC);
			F(i,mid+1,r+1){
				for(auto w:cg[i])if(w.v<0)segm.change(1,1,n,w.l,w.r,w.v);
				for(auto w:cg[i])if(w.v>=0)segm.change(1,1,n,w.l,w.r,w.v);
				for(auto &w:qs[i])ans[w.v]=max(ans[w.v],segm.ask(1,1,n,w.l,w.r)-nowC);
			}
			segm.change(1,1,n,1,n,-nowC);
		}//solver
		qq.clear();qq.shrink_to_fit();
		cc.clear();cc.shrink_to_fit();
		solve(l,mid,ql,cl),solve(mid+1,r,qr,cr);
		for(auto &v:del)segm.change(1,1,n,v.y1,v.y2,-v.v);
		del.clear();del.shrink_to_fit();
	}
	inline short main(){
		file();
		n=read(),m=read(),Q=read();
		std::vector<dpw>stq,stc;
		F(i,1,m){
			cgw[i].x1=read(),cgw[i].y1=read(),
			cgw[i].x2=read(),cgw[i].y2=read();
			cgw[i].v=read();
			stc.push_back(cgw[i]);
		}
		F(i,1,Q){
			qsw[i].x1=read(),qsw[i].y1=read(),
			qsw[i].x2=read(),qsw[i].y2=read(),
			qsw[i].v=i;
			stq.push_back(qsw[i]);
		}solve(1,n,stq,stc);
		F(i,1,Q)pi(ans[i]),pn();
		return 0;
	}
}
signed main(){return EMT::main();}

逃离藏宝洞

CE了...明明本地编译没错的说...
大概就是选择一个步长\(stp\),然后每次随便走这么多步(当然不能往回走),然后就可知到现在的深度,然后走回之前和现在的LCA处再往上走一步即可。

Code
#include "escape.h"
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<random>
#include<cassert>
typedef long long ll;typedef double db;
#define pf printf
#define F(i,a,b) for(int i=a;i<=b;i++)
#define D(i,a,b) for(int i=a;i>=b;i--)
std::mt19937 rnd((unsigned long long)(new int));
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(x>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
const int N=1e6+10;
int s[N],top;
inline void mv(int x){s[++top]=x;move(x);}
inline void rmv(){move(s[top]);top--;}
inline int ano(int x){return !x?1:(x==1?2:0);}
inline int ano(int a,int b){if(a>b)a^=b^=a^=b;return !a?(b==1?2:1):0;}
inline int random(int x){return rnd()%x;}
inline int ask(){return -query();}
void escape(int Lm,int Lq){
	int nowd=ask();bool fll=0;
	int lim=3;
	if(!fll){mv(0);int nxtd=ask();if(nxtd==nowd+1)rmv();else fll=1,nowd--;}
	if(!fll){mv(1);int nxtd=ask();if(nxtd==nowd+1)rmv();else fll=1,nowd--;}
	if(!fll)mv(2),nowd--;
	D(stp,lim,1){
		while(nowd>=stp){
			F(i,1,stp)mv(ano(ano(s[top])));
			int nxd=ask(),lst=0;
			if(nxd==nowd-stp){nowd-=stp;continue;}
			int i=(nowd+stp-nxd)>>1;
			int w=stp;
			while(w>i)lst=s[top],rmv(),w--;
			mv(ano(s[top],lst)),nowd-=i+1;
		}
	}
}

序列划分

好气啊,每次遇到这种一堆标记的就寄,不行不行,事不过三,以后不会再寄了
\(n^2\)dp挺显然的\(f_i\)表示最后一段在i结束的权值加和,则\(f_{i}=\sum\limits_{j=1}^{i-1}f_jmex(j+1,i)\)
考虑线段树优化dp。
从1到n维护一下当前位置到后面每个位置的mex值,具体就是由于mex单调不降,所以可以线段树上二分出一段删掉这个数之后的影响区间,然后将这段区间打上覆盖标记,线段树对后缀区间加和即可。

Code
#include<bits/extc++.h>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("divide.in","r",stdin);freopen("divide.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=1e6+10,mod=998244353;
	inline void add(int &x,int v){x+=v,x-=x>=mod?mod:0;}inline int M(int x){return x>=mod?x-mod:x;}
	__gnu_pbds::cc_hash_table<int,int>mp;int nxt[N],n,a[N],mex[N];bool vis[N];
	struct seg{
		#define ls p<<1
		#define rs p<<1|1
		int mn[N<<2],mx[N<<2],tag[N<<2],lz[N<<2],cov[N<<2];bool sm[N<<2];
		inline void up(int p){mn[p]=min(mn[ls],mn[rs]),mx[p]=max(mx[ls],mx[rs]);sm[p]=mn[p]==mx[p];}
		inline void build(int p,int l,int r){
			cov[p]=-1;
			if(l==r){sm[p]=1,mn[p]=mx[p]=mex[l];return;}
			int mid=(l+r)>>1;build(ls,l,mid),build(rs,mid+1,r);
			up(p);
		}
		inline void down(int p){
			if(lz[p]){
				if(sm[ls])add(tag[ls],1ll*mn[ls]*lz[p]%mod);else add(lz[ls],lz[p]);
				if(sm[rs])add(tag[rs],1ll*mn[rs]*lz[p]%mod);else add(lz[rs],lz[p]);
				lz[p]=0;
			}
			if(~cov[p])
				mn[ls]=mx[ls]=cov[ls]=cov[p],sm[ls]=1,
				mn[rs]=mx[rs]=cov[rs]=cov[p],sm[rs]=1,
				cov[p]=-1;
		}
		inline int ask(int p,int l,int r,int x){
			if(l==r)return tag[p];
			int mid=(l+r)>>1;down(p);
			if(x<=mid)return M(tag[p]+ask(ls,l,mid,x));
			else return M(tag[p]+ask(rs,mid+1,r,x));
		}
		inline void change(int p,int l,int r,int ql,int qr,int v){
			if(l>=ql&&r<=qr){if(sm[p])add(tag[p],1ll*mn[p]*v%mod);else add(lz[p],v);return;}
			int mid=(l+r)>>1;down(p);
			if(qr<=mid)change(ls,l,mid,ql,qr,v);
			else if(ql>mid)change(rs,mid+1,r,ql,qr,v);
			else change(ls,l,mid,ql,mid,v),change(rs,mid+1,r,mid+1,qr,v);
		}
		inline void cover(int p,int l,int r,int ql,int qr,int v){
			if(l>=ql&&r<=qr){
				mn[p]=mx[p]=cov[p]=v,sm[p]=1;
				return;
			}int mid=(l+r)>>1;down(p);
			if(qr<=mid)cover(ls,l,mid,ql,qr,v);
			else if(ql>mid)cover(rs,mid+1,r,ql,qr,v);
			else cover(ls,l,mid,ql,mid,v),cover(rs,mid+1,r,mid+1,qr,v);
			up(p);
		}
		inline int get(int p,int l,int r,int v){
			if(l==r)return mn[p]>=v?l:l+1;
			int mid=(l+r)>>1;down(p);
			if(mn[rs]<v)return get(rs,mid+1,r,v);
			else return get(ls,l,mid,v);
		}
		#undef ls
		#undef rs
	}segm;
	inline short main(){
		file();
		n=read();F(i,1,n)a[i]=read();
		D(i,n,1){nxt[i]=mp[a[i]];if(!nxt[i])nxt[i]=n+1;mp[a[i]]=i;}
		F(i,1,n){mex[i]=mex[i-1];if(a[i]<n)vis[a[i]]=1;while(vis[mex[i]])mex[i]++;}
		segm.build(1,1,n);
		for(int i=0;i<n;i++){
			int v=!i?1:segm.ask(1,1,n,i);
			segm.change(1,1,n,i+1,n,v);
			int st=max(segm.get(1,1,n,a[i+1]),i+2);
			if(st<=nxt[i+1]-1)segm.cover(1,1,n,st,nxt[i+1]-1,a[i+1]);
			segm.cover(1,1,n,i+1,i+1,0);
		}pi(segm.ask(1,1,n,n));
		return 0;
	}
}
signed main(){return EMT::main();}

重排列

如果两个数不互质,那么它们在B操作前后相对位置关系不会变。
那么考虑两个数\(i<j\)满足\((a_i,a_j)\not=1\),那么由\(i\)\(j\)连一条有向边,表示\(i\)\(j\)左边那么这就构成了一个\(DAG\),一个合法的拓扑序就是一组合法的排列。
\(B\)的最优策略就是每次找到最大的入度为0的点去拓扑,这个可以用优先队列维护。
\(A\)的最优策略就是给每个\(a_{i},a_j\)之间的边定向,使得拓扑序字典序尽量小,这个可以从小到大加边。

Code


#include<queue>
#include<vector>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("permutation.in","r",stdin);freopen("permutation.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=2100;bool w[N][N],vis[N];int a[N],in[N],n;std::vector<int>g[N];
	inline int gcd(int a,int b){return b?gcd(b,a%b):a;}
	inline void dfs(int x){vis[x]=1;F(i,1,n)if(!vis[i]&&w[x][i])g[x].push_back(i),in[i]++,dfs(i);}
	inline void topo(){
		std::priority_queue<int>q;F(i,1,n)if(!in[i])q.push(i);
		while(!q.empty()){int x=q.top();q.pop();pi(a[x]);for(auto y:g[x])q.push(y);}
	}
	inline short main(){
		file();
		n=read();F(i,1,n)a[i]=read();std::sort(a+1,a+n+1);
		F(i,1,n)F(j,i+1,n)if(gcd(a[i],a[j])!=1)w[i][j]=w[j][i]=1;
		F(i,1,n)if(!vis[i])dfs(i);topo();
		return 0;
	}
}
signed main(){return EMT::main();}

垫垫底底了...虽然很气,但只能明天再考回来了叭。(考不回来就后天)

黑白树

这题好女少啊!考虑对每一个联通块取最上面的点作为控制点,那么联通块加就转化成了从控制点的子树加,然后在去掉多加的贡献。
那么如何判断贡献到哪里结束呢?我们可以线段树维护区间表示的原树的点到根节点的反色点个数,这样修改时发现这个反色点个数如果多了,就说明是另外的联通块了,直接结束修改。
然后4,5操作就是普通的链加子树加了,树剖维护即可。
3操作就是区间取max,同样判断一个区间的反色点个数符不符合条件再求答案。
1操作就区间修改一下反色点个数,然后线段树上单点修改权值颜色即可。
2操作就是先求出这个点所属联通块的顶点(可以用set+树剖维护),从顶点子树加即可。

Code
#include<cassert>
#include<set>
#include<vector>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
namespace EMT{
	typedef double db;typedef long long ll;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("astill.in","r",stdin);freopen("astill.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=2e5+10;
	int n,m,dfn[N],son[N],time[N],ti,top[N],fa[N],deep[N],siz[N],v[N],col[N];std::vector<int>g[N];
	std::set<int>s[2][N];
	inline void dfs1(int x){
		siz[x]=1;deep[x]=deep[fa[x]]+1;
		for(auto y:g[x])if(y!=fa[x]){
			fa[y]=x,dfs1(y),siz[x]+=siz[y];
			if(siz[son[x]]<siz[y])son[x]=y;
		}
	}
	inline void dfs2(int x,int tp){
		top[x]=tp,time[dfn[x]=++ti]=x;
		if(!son[x])return;
		dfs2(son[x],tp);
		for(auto y:g[x])if(y!=son[x]&&y!=fa[x])
			dfs2(y,y);
	}
	struct seg{
		int s[2][N<<2],lzs[2][N<<2];
		int val[2][N<<2],lzv[2][N<<2];
		int lz[N<<2];
		inline void adds(int p,int col,int v){s[col][p]+=v,lzs[col][p]+=v;}
		inline void addv(int p,int col,int v){val[col][p]+=v,lzv[col][p]+=v;}
		inline void add(int p,int v){val[0][p]+=v,val[1][p]+=v,lz[p]+=v;}
		inline void down(int p){
			for(int i=0;i<2;i++)if(lzs[i][p])adds(p<<1,i,lzs[i][p]),adds(p<<1|1,i,lzs[i][p]),lzs[i][p]=0;
			for(int i=0;i<2;i++)if(lzv[i][p]){
				if(s[i][p<<1]==s[i][p])addv(p<<1,i,lzv[i][p]);
				if(s[i][p<<1|1]==s[i][p])addv(p<<1|1,i,lzv[i][p]);
				lzv[i][p]=0;
			}
			if(lz[p])add(p<<1,lz[p]),add(p<<1|1,lz[p]),lz[p]=0;
		}
		inline void up(int p){
			for(int i=0;i<2;i++)val[i][p]=max(s[i][p<<1]==s[i][p]?val[i][p<<1]:-1e9,s[i][p<<1|1]==s[i][p]?val[i][p<<1|1]:-1e9);
		}
		inline void cgs(int p,int l,int r,int ql,int qr,int col,int v){
			if(l>=ql&&r<=qr){adds(p,col,v);return;}
			int mid=(l+r)>>1;down(p);
			if(qr<=mid)cgs(p<<1,l,mid,ql,qr,col,v);
			else if(ql>mid)cgs(p<<1|1,mid+1,r,ql,qr,col,v);
			else cgs(p<<1,l,mid,ql,mid,col,v),cgs(p<<1|1,mid+1,r,mid+1,qr,col,v);
			up(p);
		}
		inline void cg(int p,int l,int r,int ql,int qr,int v){
			if(l>=ql&&r<=qr){add(p,v);return;}
			int mid=(l+r)>>1;down(p);
			if(qr<=mid)cg(p<<1,l,mid,ql,qr,v);
			else if(ql>mid)cg(p<<1|1,mid+1,r,ql,qr,v);
			else cg(p<<1,l,mid,ql,mid,v),cg(p<<1|1,mid+1,r,mid+1,qr,v);
			up(p);
		}
		inline void cgv(int p,int l,int r,int ql,int qr,int col,int v,int lim){
			if(s[col][p]>lim)return;
			if(l>=ql&&r<=qr){addv(p,col,v);return;}
			int mid=(l+r)>>1;down(p);
			if(qr<=mid)cgv(p<<1,l,mid,ql,qr,col,v,lim);
			else if(ql>mid)cgv(p<<1|1,mid+1,r,ql,qr,col,v,lim);
			else cgv(p<<1,l,mid,ql,mid,col,v,lim),cgv(p<<1|1,mid+1,r,mid+1,qr,col,v,lim);
			up(p);
		}
		inline int asks(int p,int l,int r,int x,int col){
			if(l==r)return s[col][p];
			int mid=(l+r)>>1;down(p);
			if(x<=mid)return asks(p<<1,l,mid,x,col);
			else return asks(p<<1|1,mid+1,r,x,col);
		}
		inline int askv(int p,int l,int r,int ql,int qr,int col,int lim){
			if(s[col][p]>lim)return 0;
			if(l>=ql&&r<=qr)return val[col][p];
			int mid=(l+r)>>1;down(p);
			if(qr<=mid)return askv(p<<1,l,mid,ql,qr,col,lim);
			else if(ql>mid)return askv(p<<1|1,mid+1,r,ql,qr,col,lim);
			else return max(askv(p<<1,l,mid,ql,mid,col,lim),askv(p<<1|1,mid+1,r,mid+1,qr,col,lim));
		}
		inline void swp(int p,int l,int r,int x){
			if(l==r){std::swap(val[0][p],val[1][p]);return;}
			int mid=(l+r)>>1;down(p);
			if(x<=mid)swp(p<<1,l,mid,x);
			else swp(p<<1|1,mid+1,r,x);
			up(p);
		}
		inline void build(int p,int l,int r){
			if(l==r){val[col[time[l]]][p]=v[time[l]];return;}
			int mid=(l+r)>>1;down(p);
			build(p<<1,l,mid),build(p<<1|1,mid+1,r);
			up(p);
		}
	}segm;
	inline void rev(int x){
		segm.cgs(1,1,n,dfn[x],dfn[x]+siz[x]-1,col[x]^1,-1);
		s[col[x]][top[x]].erase(deep[x]);
		segm.swp(1,1,n,dfn[x]);
		col[x]^=1;
		s[col[x]][top[x]].insert(deep[x]);
		segm.cgs(1,1,n,dfn[x],dfn[x]+siz[x]-1,col[x]^1,1);
	}
	inline int get(int x,int opt){
		int now=top[x],lst=0;
		while(now){
			if(*s[opt][now].begin()<=deep[x]){
				int v=*--s[opt][now].upper_bound(deep[x]);
				return v==deep[x]?lst:time[dfn[now]+v-deep[now]+1];
			}else{
				lst=now;x=fa[now],now=top[x];
			}
		}return -1;
	}
	inline void cg(int x,int y,int v){
		while(top[x]!=top[y])if(deep[top[x]]<=deep[top[y]])segm.cg(1,1,n,dfn[top[y]],dfn[y],v),y=fa[top[y]];
		else segm.cg(1,1,n,dfn[top[x]],dfn[x],v),x=fa[top[x]];
		if(deep[x]<deep[y])segm.cg(1,1,n,dfn[x],dfn[y],v);
		else segm.cg(1,1,n,dfn[y],dfn[x],v);
	}
	inline short main(){
		file();
		n=read();m=read();
		F(i,2,n){
			int x=read(),y=read();
			g[x].push_back(y),
			g[y].push_back(x);
		}dfs1(1);dfs2(1,1);
		F(i,1,n)col[i]=read(),s[col[i]][top[i]].insert(deep[i]),segm.cgs(1,1,n,dfn[i],dfn[i]+siz[i]-1,col[i]^1,1);
		F(i,1,n)v[i]=read();s[0][1].insert(0),s[1][1].insert(0);
		F(i,1,n)s[0][i].insert(1e9),s[1][i].insert(1e9);
		segm.build(1,1,n);
		while(m--){
			int opt=read();
			if(opt==1){int x=read();rev(x);}
			else if(opt==2){
				int x=read(),v=read();x=get(x,col[x]^1);
				segm.cgv(1,1,n,dfn[x],dfn[x]+siz[x]-1,col[x],v,segm.asks(1,1,n,dfn[x],col[x]));
			}else if(opt==3){
				int x=read();x=get(x,col[x]^1);
				pi(segm.askv(1,1,n,dfn[x],dfn[x]+siz[x]-1,col[x],segm.asks(1,1,n,dfn[x],col[x])));pn();
			}else if(opt==4){
				int x=read(),y=read(),v=read();
				cg(x,y,v);
			}else{
				int x=read(),v=read();
				segm.cg(1,1,n,dfn[x],dfn[x]+siz[x]-1,v);
			}
		}
		return 0;
	}
}
signed main(){return EMT::main();}

小 W 与骑士

当两个向量不共线时,显然目标\(x,y\)可以拆成两个向量加和,那么设\(f_{i}\)表示钦定第一个遇到的障碍是\(i\)的方案数,转移时容斥,带上\(-1*f_{j}\times \text{j到i方案数}\)即可。否则共线,判一下无解和无限之后记搜即可。

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("knight.in","r",stdin);freopen("knight.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=510,mod=1e9+7;
	struct dp{int x,y;friend bool operator <(dp a,dp b){return a.x+a.y<b.x+b.y;}}p[N],goal;
	int ax,ay,bx,by,K,jc[N<<1],inv[N<<1],f[N];
	inline int ksm(int a,int b){int ans=1;while(b){if(b&1)ans=1ll*a*ans%mod;a=1ll*a*a%mod;b>>=1;}return ans;}
	inline int C(int n,int m){if(n<m||m<0)return 0;return 1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;}
	inline void turn(dp &v){
		if(!v.x&&!v.y){v={0,0};return;}
		int b=round(((db)v.x*ay/(db)ax-v.y)/((db)bx*ay/(db)ax-by)),a=round((db)(v.x-b*bx)/(db)ax);
		if(a*ax+b*bx!=v.x||a*ay+b*by!=v.y||a<0||b<0)v={-1,-1};else v={a,b};
	}
	inline void add(int &x,int v){x+=v,x-=x>=mod?mod:0;}
	inline int M(int x){return x>=mod?x-mod:x;}
	namespace XI{
		inline int id(int x){return x+1000;}
		int f[2010][2010];bool w[2010][2010];
		inline bool able(int x,int y){
			return x>=-1000&&y<=1000&&x<=1000&&y>=-1000&&!w[id(x)][id(y)];
		}
		inline int dfs(int x,int y){
			if(!able(x,y))return 0;
			if(x==goal.x&&y==goal.y)return 1;
			if(~f[id(x)][id(y)])return f[id(x)][id(y)];
			return f[id(x)][id(y)]=M(dfs(x+ax,y+ay)+dfs(x+bx,y+by));
		}
		inline void solve(){
			if(1.0*goal.x/goal.y!=1.0*ax/ay){puts("0");return;}
			if(1ll*ax*bx<0){puts("-1");return;}
			if(!ax){
				std::swap(ax,ay),std::swap(bx,by);
				F(i,1,K)std::swap(p[i].x,p[i].y);
				std::swap(goal.x,goal.y);
			}
			if(ax==bx){if(goal.x%ax==0)puts("1");else puts("0");return;}
			memset(f,-1,sizeof(f)),memset(w,0,sizeof(w));
			F(i,1,K)w[id(p[i].x)][id(p[i].y)]=1;
			pi(dfs(0,0));pn();
		}
	}
	inline short main(){
		file();
		jc[0]=inv[0]=inv[1]=1;
		F(i,1,1000)jc[i]=1ll*jc[i-1]*i%mod;
		inv[1000]=ksm(jc[1000],mod-2);
		D(i,999,2)inv[i]=1ll*inv[i+1]*(i+1)%mod;
		int T=read();
		while(T--){
			goal.x=read(),goal.y=read(),K=read();
			ax=read(),ay=read(),bx=read(),by=read();
			F(i,1,K)p[i].x=read(),p[i].y=read();
			if(1ll*ay*bx==1ll*ax*by){XI::solve();continue;}
			F(i,1,K)turn(p[i]);turn(goal);
			if(goal.x<0){puts("0");continue;}
			std::sort(p+1,p+K+1);memset(f,0,sizeof(f));
			F(i,1,K)if(p[i].x>=0){
				f[i]=C(p[i].x+p[i].y,p[i].x);
				F(j,1,i-1)if(p[j].x>=0&&p[j].x<=p[i].x&&p[j].y<=p[i].y){
					add(f[i],1ll*(mod-f[j])*C(p[i].x-p[j].x+p[i].y-p[j].y,p[i].x-p[j].x)%mod);
				}
			}int ans=C(goal.x+goal.y,goal.x);
			F(i,1,K)if(p[i].x>=0&&p[i].x<=goal.x&&p[i].y<=goal.y)
				add(ans,1ll*(mod-f[i])*C(goal.x-p[i].x+goal.y-p[i].y,goal.x-p[i].x)%mod);
			pi(ans);pn();
		}
		return 0;
	}
}
signed main(){return EMT::main();}

小 Z 与函数

首先有个小结论:如果不考虑\(vis\),那么一个点的贡献是\([i,nxt_i]\)中比它大的数的个数。
证明如下:
假设现在依次有\(a,b,c\)三个数,并且现在扫到了\(a\)\((a,c)\)要造成贡献,那么由这个贡献关系可以推得\(b<a<c\)也就是说就算\(a,c\)交换了,扫到\(b\)的时候\(a\)代替了\(c\)但是仍然有贡献,也就是说不管之前如何交换,贡献是不变的。
那么如果不考虑\(vis\)的贡献,直接树状数组即可。
那么现在考虑\(vis\)的贡献。
首先有个\(n^2\)的做法是,维护当前序列的\((v,w)\)二元组,表示第i个位置现在的价值是\(v\),交换了能造成\(w\)的贡献,每次加入一个\(i\)的时候加入一个\((a_i,2)\)然后扫一下前面的二元组,能交换就交换并且加上它的\(w\)并把\(w\)变成\(1\),正确性挺好证的,无非就是把题目的第二层循环提到第一层了嘛。
那么现在改一下初值,每次加入一个\((a_i,1)\),还是从前往后不断找第一个比它小的值并且把它的\(w\)变成\(0\),也就是删除,每个点只会被删除一次,所以搭配线段树二分复杂度是\(n\log n\)的。

Code




#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("function.in","r",stdin);freopen("function.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
	const int N=2e5+10,inf=1e9;
	int n,a[N];
	struct BIT{
		int t[N];
		inline void add(int x,int v){while(x<=n)t[x]+=v,x+=x&-x;}
		inline int ask(int x){int ans=0;while(x)ans+=t[x],x-=x&-x;return ans;}
		inline int ask(int l,int r){return ask(r)-ask(l-1);}
		inline void init(){memset(t,0,sizeof(int)*(n+1));}
	}bit;
	struct seg{
		int mn[N<<2];
		inline void init(){memset(mn,0x3f,sizeof(int)*(n*4));}
		inline void insert(int p,int l,int r,int x,int v){
			if(l==r){mn[p]=v;return;}
			int mid=(l+r)>>1;
			if(x<=mid)insert(p<<1,l,mid,x,v);
			else insert(p<<1|1,mid+1,r,x,v);
			mn[p]=min(mn[p<<1],mn[p<<1|1]);
		}
		inline int ask1(int p,int l,int r,int v){
			if(l==r)return mn[p]>=v?0:l;
			int mid=(l+r)>>1;
			if(mn[p<<1]<v)return ask1(p<<1,l,mid,v);
			else return ask1(p<<1|1,mid+1,r,v);
		}
		inline int ask2(int p,int l,int r,int x){
			if(l==r)return mn[p];
			int mid=(l+r)>>1;
			if(x<=mid)return ask2(p<<1,l,mid,x);
			else return ask2(p<<1|1,mid+1,r,x);
		}
	}segm;
	inline short main(){
		file();
		int T=read();
		while(T--){
			n=read();
			F(i,1,n)a[i]=read();
			segm.init();ll ans=0;bit.init();
			F(i,1,n){
				ans+=bit.ask(a[i]-1);
				if(!bit.ask(a[i],a[i]))bit.add(a[i],1);
				segm.insert(1,1,n,i,a[i]);
				int v=0;
				while(v=segm.ask1(1,1,n,a[i])){
					if(v==i)break;
					a[i]=a[v];ans++;segm.insert(1,1,n,i,a[i]);
					segm.insert(1,1,n,v,inf);
				}pi(ans);
			}pn();
		}
		return 0;
	}
}
signed main(){return EMT::main();}

小 F 与游戏

%%%牛学长打表带师
发现答案在\(k==1\)时是\(2^{n-2}\),在\(k=n\)时是\(Catalan_{n-1}\),否则是\(2^{n-k-1}(\binom{n+k-2}{k-1}-\binom{n+k-2}{k-2})\),然后就摸了。
证明可以看madoka博客(虽然不知道会不会更),放个码跑路

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("game.in","r",stdin);freopen("game.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=1e6+10;int mod;
	inline int ksm(int a,int b){
		int ans=1;
		while(b){
			if(b&1)ans=1ll*a*ans%mod;
			a=1ll*a*a%mod;b>>=1;
		}return ans;
	}
	int jc[N],inv[N],n,k;
	inline int C(int n,int m){return 1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;}
	inline int M(int x){return x>=mod?x-mod:x;}
	inline short main(){
		file();
		jc[0]=inv[0]=inv[1]=1;
		n=read(),k=read(),mod=read();
		F(i,1,n*2)jc[i]=1ll*jc[i-1]*i%mod;
		inv[n*2]=ksm(jc[n*2],mod-2);
		D(i,n*2-1,2)inv[i]=1ll*inv[i+1]*(i+1)%mod;
		if(k==1){pi(ksm(2,n-2));return 0;}
		if(k==n){pi(M(C(n*2-2,n-1)-C(n*2-2,n-2)+mod));return 0;}
		pi(1ll*M(C(n+k-2,k-1)-C(n+k-2,k-2)+mod)*ksm(2,n-k-1)%mod);
		return 0;
	}
}
signed main(){return EMT::main();}

Minimal DFA

设左边点数是\(vl\),右边点数是\(vr\)显然\(vl\not=vr\)
对于第一问有:
如果从左往右看,每个点至多有2条出边,故\(vr\leq 2vl\)
如果从右往左看,左边每个点可以选择右边任意两个点并且选择方案不能重复故\(vl+1\leq (vr+1)^2\)
那么有了这两个限制就可以做出第一问,求出最大点数。
然后考虑下面两问。
规定一条0+1边能够带来2的权值(使方案数乘2),0/1能够带来1的权值,然后一个点的点权是它的两个出边的权值分别乘上连向的点的权值加起来,最终节点权值是1.
首先有几个结论:
在两个限制没有冲突之前:

  1. 从右往左数第\(plc\)层的点数是\(2^{2^{plc}}-1\),这个手玩一下1,3,15...即可
  2. \(plc\)层的点数是\(2^{plc}\),这个显然
  3. 从右往左数第\(i\)层权值为\(j\)的点的个数是\(\binom{2^i}{j}\),这个考虑第一层是1,2,1第二层是1,4,6,4,1...第\(i\)层是\(i-1\)层的平方即可
  4. 从右往左数第\(i\)层权值最大为\(2^{plc}\)
    发现所有的情况都可以在冲突的地方\(plc\)处被表达出来,于是在这里计算答案即可。
    那么对于左边的点,权值为\(j\)的点的个数有:\(\binom{2^{plc+1}}{j}\),证明考虑枚举两条出边分别连了什么。\(\sum\limits_{k=0}^{j}\binom{2^{plc}}{k}\binom{2^{plc}}{j-k}=\binom{2^{plc+1}}{j}\)

于是来计算答案吧:
当让答案最小时,应当让左边的点出度能是1的尽量是1否则是2并且尽量连权值小的边。
那么当\(vl<vr\)时,只需要保证右边的每个点有入度即可,答案是\(2^{2^{plc}}\times 2^{plc-1}\)。意思就是总点数乘上平均权值。

同理,\(vl<vr\),当让答案最大时,先保证右边的每个点有入度,再让剩下的点的剩下的入度都去连最大权值的点,即\(2^{2^{plc}}\times 2^{plc-1}+(vl*2-2^{2^{plc}})\times 2^{plc}\)

然后是\(vl>vr\)
我们还是一开始保证右边每个点有入度,这时相当于右边每个点都和另外一个点被捆绑着选,答案最小为空点,答案最大是权值最大点。
若要让答案最小,我们就从小往大枚举左边的点的权值\(i\in[1,2^{plc+1}]\)是多少,并且如果\(i\leq2^{plc}\)时要去掉一开始保证的那些方案数,即为\(\binom{2^{plc+1}}{i}-\binom{2^{plc}}{i(-0)}\)
若要让答案最大,我们就从大往小枚举,大同小异,方案数是\(\binom{2^{plc+1}}{i}-\binom{2^{plc}}{i-2^{plc}}\)
那么我们每次比较方案数和当前的vl,如果够了就直接输出答案,否则让vl减去方案数,答案加上这些方案的贡献。

Code


#include<vector>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("dfa.in","r",stdin);freopen("dfa.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int mod=1e8,N=1e3+100;
	int n;
	struct bigint{
		std::vector<ll>v;
		inline ll operator [](int x){return x<v.size()?v[x]:0;}
		inline int size(){return v.size();}
		friend bigint operator +(bigint a,bigint b){
			bigint C;auto &c=C.v;c.resize(max(a.size(),b.size())+3);
			for(int i=0;i<(int)c.size()-1;i++)c[i]+=a[i]+b[i],c[i+1]+=c[i]/mod,c[i]%=mod;
			while(!c.back()&&c.size()>1)c.pop_back();
			return C;
		}
		friend bigint operator +(bigint a,int b){
			bigint C;auto &c=C.v;c=a.v;c.push_back(0);c[0]+=b;
			for(int i=0;i<c.size();i++)if(c[i]>=mod)c[i+1]+=c[i]/mod,c[i]%=mod;else break;
			while(!c.back()&&c.size()>1)c.pop_back();
			return C;
		}
		friend bigint operator -(bigint a,int b){
			bigint C;auto &c=C.v;c=a.v;c.push_back(0);c[0]-=b;
			for(int i=0;i<c.size();i++)if(c[i]<0)c[i+1]-=c[i]%mod==0?-c[i]/mod:-c[i]/mod+1,c[i]+=c[i]%mod==0?mod*-c[i]/mod:mod*(-c[i]/mod+1);else break;
			while(!c.back()&&c.size()>1)c.pop_back();
			return C;
		}
		friend bigint operator -(bigint a,bigint b){
			bigint C;auto &c=C.v;c.resize(a.size()+5);
			for(int i=0;i<(int)c.size()-1;i++)c[i]+=a[i]-b[i];
			for(int i=0;i<(int)c.size()-1;i++)if(c[i]<0)c[i]+=mod,c[i+1]--;
			while(!c.back()&&c.size()>1)c.pop_back();
			return C;
		}
		friend bigint operator *(bigint a,bigint b){
			bigint C;auto &c=C.v;
			c.resize(a.size()+b.size()+5);
			for(int i=0;i<a.size();i++)
				for(int j=0;j<b.size();j++)
					c[i+j]+=a[i]*b[j];
			for(int i=0;i<(int)c.size()-1;i++)c[i+1]+=c[i]/mod,c[i]%=mod;
			while(!c.back()&&c.size()>1)c.pop_back();
			return C;
		}
		friend bigint operator *(bigint a,int b){
			bigint C;auto &c=C.v;
			c.resize(a.size()+11);
			for(int i=0;i<(int)c.size()-1;i++)c[i]+=a[i]*b,c[i+1]+=c[i]/mod,c[i]%=mod;
			while(!c.back()&&c.size()>1)c.pop_back();
			return C;
		}
		friend bigint operator /(bigint a,int b){
			bigint C;auto &c=C.v;c=a.v;
			for(int i=(int)c.size()-1;i;i--)c[i-1]+=c[i]%b*mod,c[i]/=b;c[0]/=b;
			while(!c.back()&&c.size()>1)c.pop_back();
			return C;
		}
		friend bool operator <(bigint a,bigint b){
			if(a.size()<b.size())return 1;
			if(a.size()>b.size())return 0;
			for(int i=a.size()-1;~i;i--)
				if(a[i]<b[i])return 1;
				else if(a[i]>b[i])return 0;
			return 0;
		}
		inline void print(){
			pf("%lld",v.back());
			for(int i=(int)v.size()-2;~i;i--)pf("%08lld",v[i]);putchar(' ');
		}
		inline void init(){
			static char s[10086];
			scanf("%s",s+1);int n=strlen(s+1);
			int i;
			for(i=n-7;i>=1;i-=8){
				int now=0;
				for(int j=0;j<=7;j++)now=now*10+s[i+j]-'0';
				v.push_back(now);
			}
			if(i!=-7){i+=8;int now=0;for(int j=1;j<i;j++)now=now*10+s[j]-'0';v.push_back(now);}
		}
	}a[N],b,ans,bin[N],vl,vr,C[N][N];
	inline short main(){
		 file();
		n=read();
		a[1].v.push_back(1);
		b.v.push_back(1);
		F(i,2,n+1)a[i]=a[i-1]*2;int plc=0;
		for(int i=n+1;i;i--){if(b<a[i]){a[i]=b;b=b+1;b=b*b;b=b-1;}else{plc=n-i;vl=a[i];vr=a[i+1];break;}}
		ans.v.push_back(0);
		F(i,1,n+1)ans=ans+a[i];
		bin[0].v.push_back(1);
		F(i,1,1024)bin[i]=bin[i-1]*2;
		ans.print();
		C[0][0].v.push_back(1);
		F(i,1,1024){
			C[i][0].v.push_back(1);
			F(j,1,i)C[i][j]=C[i-1][j]+C[i-1][j-1];
		}
		bigint vvl=vl;
		if(vl<vr){
			(bin[(1<<plc)-1]*(1<<plc)).print();
		}else{
			vl=vl-vr;
			bigint an=bin[(1<<plc)-1]*(1<<plc);
			for(int i=1;i<=(1<<(plc+1));i++){
				bigint cur=C[1<<(plc+1)][i];
				if(i<=(1<<plc))cur=cur-C[1<<(plc)][i];
				if(vl<cur){an=an+vl*i;break;}
				else an=an+cur*i,vl=vl-cur;
			}an.print();
		}
		vl=vvl;
		if(vl<vr){
			((bin[(1<<plc)-1])*(1<<(plc))+(vl*2-bin[1<<plc]+1)*(1<<plc)).print();
		}
		else{
			vl=vl-vr;
			bigint an=bin[(1<<plc)-1]*(1<<(plc))+(bin[(1<<plc)]-1)*(1<<(plc));
			for(int i=(1<<(plc+1));i>=1;i--){
				bigint cur=C[1<<(plc+1)][i];
				if(i>=(1<<plc))cur=cur-C[1<<(plc)][i-(1<<plc)];
				if(vl<cur){an=an+vl*i;break;}
				else an=an+cur*i,vl=vl-cur;
			}an.print();
		}
		return 0;
	}
}
signed main(){return EMT::main();}

启程的日子

发现n,m>=2的时候答案最多只有3 (鬼知道怎么发现的)
那么答案是0,1的直接判走,2的尝试寻找一个由0构成的联通块挨着所有1然后加上并集再减去0的交集即可。
3的尝试构造+ + -,第一次把第一行全设成\(1\)\([2,n-1]\)行的,如果列是奇数那么都是1,否则原来是啥就填啥,最后一行都是0
第二次把第一行全设成\(0\)\([2,n-1]\)行的,如果列是偶数那么都是1,否则原来是啥就填啥,最后一行都是1
这样保证了\([2,n-1]\)所有1都被覆盖了2次,0都被覆盖了1次,
第三次把\([2,n-1]\)全设成\(1\),剩下的取反填即可。

Code
#include<vector>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("bitbit.in","r",stdin);freopen("bitbit.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d",x);}inline void pn(){pf("\n");}
	const int N=510;
	int goal[N][N],cnt[2],n,m,id[N][N],fa[N*N],icnt,siz[N*N],vis[N*N];
	struct dp{int x,y;};
	std::vector<dp>in[N*N];
	inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
	inline void merge(int x,int y){
		int fx=find(x),fy=find(y);
		if(fx==fy)return;
		if(in[fx].size()>in[fy].size())in[fy].swap(in[fx]);
		for(auto v:in[fx])in[fy].push_back(v);
		in[fx].clear();in[fx].shrink_to_fit();
		siz[fy]+=siz[fx];fa[fx]=fy;
	}
	inline void check(int x){
		int sum=0;
		for(auto w:in[x]){
			if(goal[w.x-1][w.y]&&vis[find(id[w.x-1][w.y])]!=x)vis[find(id[w.x-1][w.y])]=x,sum+=siz[find(id[w.x-1][w.y])];
			if(goal[w.x+1][w.y]&&vis[find(id[w.x+1][w.y])]!=x)vis[find(id[w.x+1][w.y])]=x,sum+=siz[find(id[w.x+1][w.y])];
			if(goal[w.x][w.y-1]&&vis[find(id[w.x][w.y-1])]!=x)vis[find(id[w.x][w.y-1])]=x,sum+=siz[find(id[w.x][w.y-1])];
			if(goal[w.x][w.y+1]&&vis[find(id[w.x][w.y+1])]!=x)vis[find(id[w.x][w.y+1])]=x,sum+=siz[find(id[w.x][w.y+1])];
		}if(sum==cnt[1]){
			puts("2\n+");
			F(i,1,n){
				F(j,1,m){
					if(goal[i][j]||find(id[i][j])==x)putchar('1');
					else putchar('0');
				}pn();
			}puts("-");
			F(i,1,n){
				F(j,1,m){
					if(find(id[i][j])==x)putchar('1');
					else putchar('0');
				}pn();
			}exit(0);
		}
	}
	namespace XI{
		inline void print(int l,int r){
			puts("+");
			F(i,1,l-1)putchar('0');
			F(i,l,r)putchar('1');
			F(i,r+1,m)putchar('0');pn();
		}
		inline void solve(){
			int fl=0;
			int cnt=0;
			F(i,1,m){
				if(!goal[1][i])fl=0;
				else if(!fl)fl=1,cnt++;
			}pi(cnt);pn();fl=0;
			F(i,1,m){
				if(!goal[1][i]){if(!fl)continue;else print(fl,i-1),fl=0;}
				else if(!fl)fl=i;
			}if(fl)print(fl,m);
		}
	}
	namespace XII{
		inline void print(int l,int r){
			puts("+");
			F(i,1,l-1)putchar('0'),pn();
			F(i,l,r)putchar('1'),pn();
			F(i,r+1,n)putchar('0'),pn();
		}
		inline void solve(){
			int fl=0;
			int cnt=0;
			F(i,1,n){
				if(!goal[i][1])fl=0;
				else if(!fl)fl=1,cnt++;
			}pi(cnt);pn();fl=0;
			F(i,1,n){
				if(!goal[i][1]){if(!fl)continue;else print(fl,i-1),fl=0;}
				else if(!fl)fl=i;
			}if(fl)print(fl,n);
		}
	}
	namespace XIII{
		inline void solve(){
			puts("3");
			if(n!=2){
				puts("+");
				F(i,1,m)putchar('0');putchar('\n');
				F(i,2,n-1){
					F(j,1,m)if((j&1)||goal[i][j])putchar('1');else putchar('0');
					putchar('\n');
				}
				F(i,1,m)putchar('1');putchar('\n');
				puts("+");
				F(i,1,m)putchar('1');putchar('\n');
				F(i,2,n-1){
					F(j,1,m)if(!(j&1)||goal[i][j])putchar('1');else putchar('0');
					putchar('\n');
				}
				F(i,1,m)putchar('0');putchar('\n');
				puts("-");
				F(i,1,m)if(goal[1][i])putchar('0');else putchar('1');putchar('\n');
				F(i,2,n-1){
					F(j,1,m)putchar('1');
					putchar('\n');
				}
				F(i,1,m)if(goal[n][i])putchar('0');else putchar('1');putchar('\n');
			}else{
				puts("+");
				putchar('1');F(i,2,m-1)putchar('1');putchar('0');putchar('\n');
				putchar('1');F(i,2,m-1)putchar(goal[2][i]+'0');putchar('0');putchar('\n');
				puts("+");
				putchar('0');F(i,2,m-1)putchar(goal[1][i]+'0');putchar('1');putchar('\n');
				putchar('0');F(i,2,m-1)putchar('1');putchar('1');putchar('\n');
				puts("-");
				putchar(!goal[1][1]+'0');F(i,2,m-1)putchar('1');putchar(!goal[1][m]+'0');putchar('\n');
				putchar(!goal[2][1]+'0');F(i,2,m-1)putchar('1');putchar(!goal[2][m]+'0');putchar('\n');
			}
		}
	}
	inline short main(){
		file();
		n=read(),m=read();
		F(i,1,n)F(j,1,m){
			char ch=getchar();
			while(ch!='0'&&ch!='1')ch=getchar();
			goal[i][j]=ch-'0';id[i][j]=++icnt;fa[icnt]=icnt,siz[icnt]=1;
			cnt[goal[i][j]]++;in[icnt].push_back({i,j});
		}
		if(n==1){XI::solve();return 0;}
		if(m==1){XII::solve();return 0;}
		if(!cnt[1]){puts("0");return 0;}
		F(i,1,n)F(j,1,m){
			if(!goal[i][j]){
				if(i<n&&!goal[i+1][j])merge(id[i+1][j],id[i][j]);
				if(j<m&&!goal[i][j+1])merge(id[i][j+1],id[i][j]);
			}else{
				if(goal[i+1][j])merge(id[i+1][j],id[i][j]);
				if(goal[i][j+1])merge(id[i][j+1],id[i][j]);
				if(siz[find(id[i][j])]==cnt[1]){puts("1\n+");F(i,1,n){F(j,1,m)putchar(goal[i][j]+'0');pn();}return 0;}
			}
		}
		F(i,1,n)F(j,1,m)if(!goal[i][j]){
			if(!vis[find(id[i][j])])
				vis[find(id[i][j])]=1,
				check(find(id[i][j]));
		}XIII::solve();
		return 0;
	}
}
signed main(){return EMT::main();}

爆搜

考虑把2i-1和2i绑在一起,然后试图把整个图练成若干个环和链,容易发现 (其实是我不会证) 每个方案都代表了原图的一种方案。那么考虑分链和环dp,链的情况设\(f_{S,i}\)表示用了集合\(S\),链尾是\(i\)的方案数(带权),转移显然然后统计到答案里面。
环的就是链的加了一个钦定最小编号的是链头,统计到答案里面即可。
最后子集卷积,为了避免算重,用\(dp\)数组和卷积数组卷,并且钦定dp数组的下标中必须有自己下标的某个编号即可。

Code
#include<vector>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("dfs.in","r",stdin);freopen("dfs.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int mod=1e9+7,inv2=(mod+1)>>1;
	int f1[1<<18][40],f2[1<<18][40],n,m,c,f[1<<18],rev[1<<18],s[1<<18];std::vector<int>g[40];bool w[40][40];
	inline int A(int x){return x*2-1;}inline int B(int x){return x*2;}
	inline int lb(int x){return x&-x;}inline int bel(int x){return (x+1)>>1;}
	inline void add(int &x,int v){x+=v,x-=x>=mod?mod:0;}
	inline void solve1(){
		F(i,1,n)f1[1<<(bel(i)-1)][i]=1;
		for(int i=1;i<1<<(bel(n));i++)F(j,1,n)if(f1[i][j]){
			for(auto &k:g[j])if(!(i&(1<<(bel(k)-1)))){
				if(k&1)add(f1[i|(1<<(bel(k)-1))][B(bel(k))],1ll*f1[i][j]*c%mod);
				else add(f1[i|(1<<(bel(k)-1))][A(bel(k))],1ll*f1[i][j]*c%mod);
			}if(i!=lb(i))add(f[i],1ll*f1[i][j]*inv2%mod);
		}
	}
	inline void solve2(){
		F(i,1,n)if((i&1)==0)f2[1<<(bel(i)-1)][i]=1;
		for(int i=1;i<1<<bel(n);i++)F(j,1,n)if(f2[i][j]){
			for(auto &k:g[j])if(!(i&(1<<(bel(k)-1)))&&(1<<(bel(k)-1))>lb(i)){
				if(k&1)add(f2[i|(1<<(bel(k)-1))][B(bel(k))],1ll*f2[i][j]*c%mod);
				else add(f2[i|(1<<(bel(k)-1))][A(bel(k))],1ll*f2[i][j]*c%mod);
			}if(w[j][A(rev[lb(i)])])add(f[i],1ll*f2[i][j]*c%mod);
		}
	}
	inline void solve(){
		s[0]++;int ans=0;
		for(int i=1;i<1<<bel(n);i++){
			for(int j=i;j;j=(j-1)&i)if(j&lb(i)){
				add(s[i],1ll*f[j]*s[i^j]%mod);
			}add(ans,s[i]);
		}pi(ans+1);pn();
	}
	inline short main(){
		file();
		n=read(),m=read(),c=read();if(n&1)++n;
		F(i,1,18)rev[1<<(i-1)]=i;
		F(i,1,m){
			int x=read(),y=read();
			g[x].push_back(y),g[y].push_back(x);
			w[x][y]=w[y][x]=1;
		}
		solve1(),solve2(),solve();
		return 0;
	}
}
signed main(){return EMT::main();}

神必的集合

发现如果把线性基建出来,一个数的排名二进制上的第\(i\)位是1的话,对应的线性基上的第\(i\)个主元的位置就是1.
那么可以根据这个进行dp.设\(f_{i,j}\)表示考虑了前\(i\)位,从中选取了\(j\)个作为主元的方案数。
首先判断一下这个位当主元是否合法,就是根据给出条件的每个数,如果当主元还能不能符合条件。
然后如果给出条件中要求这是主元只能进行“是主元”的转移,
否则可以根据是否合法进行“是主元”和“不是主元”的转移,然后考虑线性基这个主元其它自由元可以任意选,所以带上系数\(2^{i-j}\)

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("set.in","r",stdin);freopen("set.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=210,mod=998244353;
	int n,m,w;ll x[N],y[N],bit[65];int f[N][N];
	inline void ins(ll v){
		D(i,63,0)if(v>>i&1){
			if(!bit[i]){bit[i]=v;return;}
			v^=bit[i];
		}
	}
	inline void add(int &x,int v){x+=v,x-=x>=mod?mod:0;}
	inline short main(){
		file();
		n=read(),m=read();
		F(i,1,m){
			x[i]=read()-1,y[i]=read();
			if(x[i]){
				w=max(w,64-__builtin_clzll(x[i]));
				ins(y[i]);
			}else {i--,m--;}
		}
		f[0][0]=1;
		F(i,0,n-1)F(j,0,i)if(f[i][j]){
			int fl=1;
			F(k,1,m)fl&=(y[k]>>i&1)==(x[k]>>j&1);
			if(bit[i]){
				add(f[i+1][j+1],fl*f[i][j]);
			}else{
				add(f[i+1][j+1],(1ll<<i>>j)%mod*fl*f[i][j]%mod);
				add(f[i+1][j],f[i][j]);
			}
		}
		int ans=0;
		F(i,w,n)add(ans,f[n][i]);
		pi(ans);
		return 0;
	}
}
signed main(){return EMT::main();}

法阵

原来付gg的做法是假的啊...白写了
贴个假代码跑路了

Code


#include<vector>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("fz.in","r",stdin);freopen("fz.out","w",stdout);}
	inline ll max(ll a,ll b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
	const int N=5e5+10,B=18;
	int n,a[N],s[N],m,st[N][20],log[N],rt[N],size;std::vector<int>plc[N],now,p;
	inline int getmax(int l,int r){int lg=log[r-l+1];return max(st[l+(1<<lg)-1][lg],st[r][lg]);}
	struct seg{
		int ls[N*60],rs[N*60],siz[N*60],tot;
		inline void insert(int x,int &y,int l,int r,int v){
			y=++tot,ls[y]=ls[x],rs[y]=rs[x],siz[y]=siz[x]+1;
			if(l==r)return;int mid=(l+r)>>1;
			if(v<=mid)insert(ls[x],ls[y],l,mid,v);
			else insert(rs[x],rs[y],mid+1,r,v);
		}
		inline void ask(int x,int y,int l,int r){
			if(!size||siz[x]==siz[y])return;
			if(l==r){now.push_back(l);size-=min(size,siz[y]-siz[x]);return;}
			int mid=(l+r)>>1;
			ask(rs[x],rs[y],mid+1,r);if(size)ask(ls[x],ls[y],l,mid);
		}
	}segm;
	inline short main(){
		file();
		n=read();
		F(i,1,n)a[i]=read(),s[i]=a[i];
		std::sort(s+1,s+n+1);s[0]=std::unique(s+1,s+n+1)-s-1;
		F(i,1,n)a[i]=std::lower_bound(s+1,s+s[0]+1,a[i])-s;
		log[0]=-1;F(i,1,n)log[i]=log[i>>1]+1,st[i][0]=s[a[i]],plc[a[i]].push_back(i);
		F(i,1,n)F(j,1,log[n]){st[i][j]=st[i][j-1];int k=i-(1<<(j-1));if(k>0)st[i][j]=max(st[i][j],st[k][j-1]);}
		F(i,1,n)segm.insert(rt[i-1],rt[i],1,s[0],a[i]);
		m=read();
		while(m--){
			now.clear(),p.clear();int l=read(),r=read();size=B;segm.ask(rt[l-1],rt[r],1,s[0]);
			for(auto v:now){
				auto it=std::lower_bound(plc[v].begin(),plc[v].end(),l);
				for(;it!=plc[v].end()&&*it<=r;it++)p.push_back(*it);
			}std::sort(p.begin(),p.end());ll ans=0;
			for(int i=0;i<p.size();i++){
				for(int j=i+1;j<p.size();j++){
					if(p[j]-p[i]<=1)continue;
					ans=max(ans,(ll)s[a[p[j]]]+(ll)s[a[p[i]]]+(ll)getmax(p[i]+1,(p[i]+p[j])>>1));
				}
			}pi(ans);pn();
		}
		return 0;
	}
}
signed main(){return EMT::main();}

旅行

考场上确实是想到点分树了,但是没有想好点更新的先后顺序(我是\(\uparrow \downarrow\)吗??)
具体就是对点分树上每个点维护子树内和自己从小到大排序的点的一个指针,每次取出\(dis_{i}+c_{i}\)最小的点暴力跳\(fa\)去更新能更新到的点,表现是移动指针和改变dis,推入队列。
对非树边任取一个点维护所有点到它的距离指针,方法是一样的。
如果所有的距离排序都通过bfs实现可以做到\(n\log n+nk\),但是太懒了于是多了个\(\log\)

Code


#include<vector>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("travel.in","r",stdin);freopen("travel.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
	const int N=2e5+100;
	int from[N],to[N],n,m,d[N],c[N];std::vector<int>g[N],ban;
	struct dp{int x;ll v;friend bool operator <(dp a,dp b){return a.v>b.v;}};
	namespace bmdq{
		int fa[N];
		inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
		inline void init(){
			F(i,1,n)fa[i]=i;
			F(i,1,m){
				int x=from[i],y=to[i],fx=find(x),fy=find(y);
				if(fx==fy){ban.push_back(i);continue;}
				fa[fx]=fy;g[x].push_back(y),g[y].push_back(x);
			}
		}
	}
	namespace dd{
		int deep[N],st[N<<1][20],log[N<<1],dfn[N],ti;
		inline void dfs(int x,int fa){
			st[dfn[x]=++ti][0]=x;
			for(auto y:g[x])if(y!=fa)
				deep[y]=deep[x]+1,dfs(y,x),st[++ti][0]=x;
		}
		inline int dmin(int a,int b){return deep[a]<deep[b]?a:b;}
		inline void rmq_init(int n){
			log[0]=-1;
			F(i,1,n)log[i]=log[i>>1]+1;
			F(i,1,n)F(j,1,log[n]){
				st[i][j]=st[i][j-1];int k=i-(1<<(j-1));
				if(k>0)st[i][j]=dmin(st[i][j],st[k][j-1]);
			}
		}
		inline int getlca(int x,int y){
			x=dfn[x],y=dfn[y];if(x>y)x^=y^=x^=y;
			int lg=log[y-x+1];
			x=st[x+(1<<lg)-1][lg],y=st[y][lg];
			return dmin(x,y);
		}
		inline int getdis(int x,int y){return deep[x]+deep[y]-deep[getlca(x,y)]*2;}
		inline void init(){dfs(1,0);rmq_init(ti);}
	}using dd::getdis;
	ll dis[N];std::priority_queue<dp>q;bool vis[N];
	namespace band{
		std::vector<int>g[N];int dis[55][N],key[55];std::vector<dp>in[N];
		inline void bfs(int i,int xx){
			static int q[N],hd,tl;q[hd=tl=1]=xx;
			memset(dis[i],0x3f,sizeof(int)*(n+1));dis[i][xx]=0;
			while(hd<=tl){
				int x=q[hd++];in[i].push_back({x,dis[i][x]});
				for(auto y:g[x])if(dis[i][y]>dis[i][x]+1)
					dis[i][y]=dis[i][x]+1,q[++tl]=y;
			}
		}
		inline void init(){for(int i=0;i<ban.size();i++)bfs(i,from[ban[i]]);}
		inline void upd(int x,ll v){
			for(int i=0;i<ban.size();i++){
				while(key[i]<in[i].size()){
					if(vis[in[i][key[i]].x]){key[i]++;continue;}
					if(in[i][key[i]].v+dis[i][x]<=d[x]){
						vis[in[i][key[i]].x]=1;
						EMT::dis[in[i][key[i]].x]=v;
						q.push({in[i][key[i]].x,v+c[in[i][key[i]].x]});
						key[i]++;
					}else break;
				}
			}
		}
	}
	namespace tree{
		int fa[N],siz[N],rt,maxn,mx[N],key[N];
		std::vector<dp>in[N];
		inline void getsiz(int x,int fa){
			siz[x]=1;
			for(auto y:g[x])if(!vis[y]&&y!=fa)
				getsiz(y,x),siz[x]+=siz[y];
		}
		inline void findrt(int x,int fa){
			siz[x]=1,mx[x]=0;
			for(auto y:g[x])if(!vis[y]&&y!=fa){
				findrt(y,x),siz[x]+=siz[y],mx[x]=max(mx[x],siz[y]);
			}mx[x]=max(mx[x],maxn-siz[x]);if(mx[x]<mx[rt])rt=x;
		}
		inline void dfs(int x){
			vis[x]=1;
			for(auto y:g[x])if(!vis[y]){
				getsiz(y,x),maxn=siz[y],mx[rt=0]=maxn+1,findrt(y,x);
				fa[rt]=x;dfs(rt);
			}
		}
		inline void init(){
			mx[rt=0]=n+1,maxn=n;
			findrt(1,0),dfs(rt);
			F(x,1,n)for(int i=x;i;i=fa[i])in[i].push_back({x,getdis(x,i)});
			F(i,1,n)std::sort(in[i].begin(),in[i].end(),[](dp a,dp b){return a.v<b.v;});
		}
		inline void dij(){
			memset(vis,0,sizeof(vis));
			q.push({1,c[1]});vis[1]=1;
			while(!q.empty()){
				int x=q.top().x;ll w=q.top().v;q.pop();
				//pi(x);pi(w);pi(114514);pn();
				for(int i=x;i;i=fa[i]){
					int Dis=getdis(i,x);
					while(key[i]<in[i].size()){
						if(vis[in[i][key[i]].x]){key[i]++;continue;}
						if(in[i][key[i]].v+Dis<=d[x]){
							dis[in[i][key[i]].x]=w,
							vis[in[i][key[i]].x]=1,
							q.push({in[i][key[i]].x,w+c[in[i][key[i]].x]});
							key[i]++;
						}else break;
					}
				}band::upd(x,w);
			}
		}
	}
	inline short main(){
		file();
		n=read(),m=read();
		F(i,1,m)from[i]=read(),to[i]=read(),band::g[from[i]].push_back(to[i]),band::g[to[i]].push_back(from[i]);
		bmdq::init(),dd::init(),tree::init(),band::init();
		F(i,1,n)d[i]=read(),c[i]=read();
		tree::dij();
		F(i,2,n)pi(dis[i]),pn();
		return 0;
	}
}
signed main(){return EMT::main();}

本来分出来之前以为自己要爆蛋来着,结果T3硬是给了背包40...

首先将转化关系看成连有向边,然后缩点成强连通分量,发现如果一个强连通分量中有空点或者没有出边,大小为1则这个强连通分量连出去的边和内部的边都可以赖掉,那么剩下的满了的强连通分量必需把自己的东西转移给相连的分量,于是考虑网络流求出最大避免的损失。
那么肯定是源点向满的分量入点连1流量,出点向汇点连1流量,不满的分量向出点连不满几个,另外的强连通分量之间的连边\(a\to b\)转化成\(a_{in}\to b_{out},a_{out}\to b_{out}\)。那么跑一遍网络流即可。

Code
#include<vector>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	#define pb push_back
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("graph.in","r",stdin);freopen("graph.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=150,inf=0x3f3f3f3f;
	char s[N];int n,m,ans,st[65],out[N];
	inline int id(char ch){return ch>='a'&&ch<='z'?ch-'a'+1:(ch>='A'&&ch<='Z'?ch-'A'+27:(ch-'0'+53));}
	inline char gc(){char ch=getchar();while(!(ch>='a'&&ch<='z')&&!(ch>='A'&&ch<='Z')&&!(ch>='0'&&ch<='9'))ch=getchar();return ch;}
	namespace dinic{
		int head[N],co=1,S,T,Hd[N],dis[N];
		struct node{int next,to,w;}e[N*N];
		inline void add(int next,int to,int w){e[++co]={head[next],to,w},head[next]=co;}
		inline void ins(int next,int to,int w){add(next,to,w),add(to,next,0);}
		inline void clear(){memset(head,0,sizeof(int)*(T+1));co=1;}
		inline bool bfs(){
			static int q[N],hd,tl;
			memcpy(head,Hd,sizeof(int)*(T+1));q[hd=tl=1]=S;
			memset(dis,0x3f,sizeof(int)*(T+1));dis[S]=0;
			while(hd<=tl){
				int x=q[hd++];
				for(int i=head[x],j;i;i=e[i].next)if(e[i].w){
					if(dis[j=e[i].to]>dis[x]+1){
						dis[j]=dis[x]+1;
						q[++tl]=j;
					}
				}if(x==T)return 1;
			}return 0;
		}
		inline int dfs(int x,int in){
			if(x==T)return in;
			int rest=in,go;
			for(int i=head[x],j;i;head[x]=i=e[i].next)if(e[i].w){
				if(dis[j=e[i].to]==dis[x]+1){
					go=dfs(j,min(rest,e[i].w));
					if(go)rest-=go,e[i].w-=go,e[i^1].w+=go;
					else dis[j]=0;
				}if(!rest)break;
			}return in-rest;
		}
		inline int dinic(){
			memcpy(Hd,head,sizeof(int)*(T+1));
			int ans=0;while(bfs())ans+=dfs(S,inf);
			return ans;
		}
	}
	namespace tarjan{
		std::vector<int>g[N];bool vis[N];
		int gcc,bel[N],dfn[N],ti,low[N],s[N],top,rsiz[N],psiz[N];
		inline void clear(){
			F(i,1,gcc)rsiz[i]=psiz[i]=0;
			F(i,1,62)g[i].clear(),bel[i]=dfn[i]=low[i]=0;
			gcc=ti=0;
		}
		inline void tj(int x){
			dfn[x]=low[x]=++ti;
			vis[x]=1;s[++top]=x;
			for(auto y:g[x])if(!dfn[y]){
				tj(y),low[x]=min(low[x],low[y]);
			}else if(vis[y])low[x]=min(low[x],dfn[y]);
			if(dfn[x]==low[x]){
				int y;gcc++;
				do{
					y=s[top--];vis[y]=0;bel[y]=gcc;
					psiz[gcc]++;if(st[y])rsiz[gcc]++;
				}while(y!=x);
			}
		}
		inline void init(){
			F(i,1,62)if(!dfn[i])tj(i);
		}
	}
	inline short main(){
		file();
		int T=read();
		while(T--){
			tarjan::clear();
			dinic::clear();
			scanf("%s",s+1);n=strlen(s+1);m=read();
			memset(st,0,sizeof(st));
			memset(out,0,sizeof(out));
			F(i,1,n)st[id(s[i])]=1;
			int ans=0;
			F(i,1,62)if(st[i])ans++;
			F(i,1,m){
				int x=id(gc()),y=id(gc());
				tarjan::g[x].push_back(y);
			}tarjan::init();dinic::S=tarjan::gcc*2+1,dinic::T=tarjan::gcc*2+2;
			F(x,1,62)for(auto y:tarjan::g[x])
			if(tarjan::bel[x]!=tarjan::bel[y])out[tarjan::bel[x]]++;
			int gcc=tarjan::gcc;
			F(i,1,gcc){
				if(tarjan::psiz[i]!=tarjan::rsiz[i]){
					dinic::ins(i+gcc,dinic::T,tarjan::psiz[i]-tarjan::rsiz[i]);
				}else if(tarjan::psiz[i]==tarjan::rsiz[i]&&(out[i]||tarjan::psiz[i]>1)){
					ans--,dinic::ins(dinic::S,i,1),dinic::ins(i+gcc,dinic::T,1);
				}
			}
			F(x,1,62)for(auto y:tarjan::g[x])if(tarjan::bel[x]!=tarjan::bel[y]){
				int fx=tarjan::bel[x],fy=tarjan::bel[y];
				dinic::ins(fx,fy+gcc,inf);
				dinic::ins(fx+gcc,fy+gcc,inf);
			}
			ans+=dinic::dinic();
			pi(ans);pn();
		}
		return 0;
	}
}
signed main(){return EMT::main();}

想不出

首先肯定不能出现若两个点本来能够交上,但是现在它们的方向都是能交上时的反方向的情况。
那么判断一个点的朝向可以根据左边能和自己交上的多还是右边的多,哪边多就取哪边。
\(O(n^2)\)暴力即可。

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("surface.in","r",stdin);freopen("surface.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
	const int N=2e3+10;const db sq3=std::sqrt(3);
	int n,opt[N],ans;
	struct pt{int x,y;friend bool operator <(pt a,pt b){return a.x<b.x;}}p[N];
	inline db getb(db k,db x,db y){return y-k*x;}
	inline db getx(db k1,db b1,db k2,db b2){return (b2-b1)/(k1-k2);}
	inline short main(){
		file();
		n=read();
		F(i,1,n)p[i].x=read(),p[i].y=read();
		std::sort(p+1,p+n+1);
		F(i,1,n){
			int cntl=0,cntr=0;
			F(j,1,i-1){
				db jx=getx(-sq3,getb(-sq3,p[j].x,p[j].y),sq3,getb(sq3,p[i].x,p[i].y));
				if(jx>=p[j].x&&jx<=p[i].x)cntl++;
			}
			F(j,i+1,n){
				db jx=getx(-sq3,getb(-sq3,p[j].x,p[j].y),sq3,getb(sq3,p[i].x,p[i].y));
				if(jx>=p[i].x&&jx<=p[j].x)cntr++;
			}opt[i]=cntl>=cntr?1:2;
		}
		F(i,1,n){
			db k1=opt[i]==1?sq3:-sq3;
			F(j,i+1,n)if(opt[i]!=opt[j]){
				db k2=opt[i]==1?-sq3:sq3,jx=getx(k1,getb(k1,p[i].x,p[i].y),k2,getb(k2,p[j].x,p[j].y));
				if(jx>=p[i].x&&jx<=p[j].x)ans++;
			}
		}pi(ans-n+1);
		return 0;
	}
}
signed main(){return EMT::main();}

分裂

首先,砸一个编号为\(i\)的球相当于让总数增加\(i\)
先考虑根号的暴力:
每个编号的球之砸一个,砸到再砸就超过了,这时候肯定剩下一个\(v<=i\),那么如果\(v\not=1\)\(v\)砸了就行,否则把\(i\)变回去,砸一个\(2\)就行。
那么发现过程中其实每一个编号的球留一个就行,于是每个编号的球砸到只剩下一个,还是看剩下的\(v\)是多少,如果不是1直接砸\(v\),否则如果砸完多于0个\(i\)之后,\(v\)才等于1,可以选择少砸一个\(i\),多砸一个\(i-1\),否则可以少砸一个\(i-1\)多砸一个\(i\)

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define int __int128
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("split.in","r",stdin);freopen("split.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pn(){pf("\n");}
	inline void write(int x){if(x>=10)write(x/10);putchar(x%10+'0');}
	int n,cnt[60],now;
	inline short main(){
		file();
		int T=read();
		while(T--){
			n=read();memset(cnt,0,sizeof(cnt));
			if(n==3||n==5||n==8){puts("-1");continue;}
			if(n==1){puts("1\n1 1");continue;}
			if(n==2){puts("1\n2 2");continue;}
			cnt[2]=2;now=2;
			for(int i=2;i<60;i++){
				int num=cnt[i]-1;
				if(num*i+now<n){now+=num*i;cnt[i]=1;cnt[i+1]+=num*(i+1);}
				else{
					if((n-now)%i==0){
						int v=(n-now)/i;
						cnt[i]-=v;cnt[i+1]+=v*(i+1);
					}else if((n-now)%i==1){
						if(n-now==1){
							cnt[i-1]++,cnt[i]-=i,
							cnt[i]--,cnt[i+1]+=i+1;
						}else{
							int v=(n-now)/i;
							cnt[i]-=v-1,cnt[i+1]+=(v-1)*(i+1);
							cnt[i-1]--,cnt[i]+=i;
							cnt[2]--,cnt[3]+=3;
						}
					}else{
						int v=(n-now)/i,w=(n-now)%i;
						cnt[i]-=v,cnt[i+1]+=v*(i+1);
						cnt[w]--,cnt[w+1]+=w+1;
					}break;
				}
			}int co=0;
			for(int i=1;i<60;i++)if(cnt[i])co++;
			write(co);pn();
			for(int i=1;i<60;i++)if(cnt[i])write(i),pf(" "),write(cnt[i]),pn();
		}
		return 0;
	}
}
signed main(){return EMT::main();}

未来

定义\(a\$b\to(a+b)*2\%3\),则若把三种颜色看成012每一轮就是每个点进行一下这个运算。
发现m轮后x变成\(\sum\limits_{i=0}^{m}\binom{m}{i}a_{(x+i)\bmod n}\)
然后由于模数是3所以考虑3进制拆分,根据卢卡斯定理只有\(3^i\)\(0\)有贡献。

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define int long long
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("future.in","r",stdin);freopen("future.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=5e5+10;
	int n,m,a[N],tmp[N];
	inline short main(){
		file();
		n=read(),m=read();
		F(i,1,n){
			char ch=getchar();
			while(ch!='r'&&ch!='g'&&ch!='b')ch=getchar();
			a[i-1]=ch=='r'?0:(ch=='g'?1:2);
		}
		for(int p=1;p<=m;p*=3){
			int b=m/p%3;
			while(b--){
				for(int i=0;i<n;i++)tmp[i]=(3-(a[(i+p)%n]+a[i])%3)%3;
				for(int i=0;i<n;i++)a[i]=tmp[i];
			}
		}
		for(int i=0;i<n;i++)putchar(a[i]?(a[i]==1?'g':'b'):'r');
		return 0;
	}
}
signed main(){return EMT::main();}

回忆

由于\(n\times m<=40\)所以让\(n,m\)较大值为\(n\),较小值为\(m\),则\(m<=6\)即联通块最多有\(3\)
\(f_{i,S,a_1,a_2,a_3,a_4}\)表示dp到第i行第j列所属联通块为\(S_j\),三个联通块大小分别为\(a_1,a_2,a_3\),dp过程中出现的最大联通块的大小为\(a_4\)的概率,
转移就是枚举下一行哪一个是1哪一个是0,用并查集维护即可。

Code
#include<map>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("memory.in","r",stdin);freopen("memory.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=45,mod=998244353;
	int n,m,a[N][N],b[N][N];
	struct node{
		int a[5];
		friend bool operator <(node a,node b){
			return a.a[1]!=b.a[1]?a.a[1]<b.a[1]:(a.a[2]!=b.a[2]?a.a[2]<b.a[2]:(a.a[3]!=b.a[3]?a.a[3]<b.a[3]:a.a[4]<b.a[4]));
		}
		inline void print()const{
			F(i,1,4)pi(a[i]);pn();
		}
	}nxt;
	std::map<node,int>f[N][1<<12];bool vis[N];
	int fa[N],siz[N],bin[N],id[N];inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
	inline void add(int &x,int v){x+=v,x-=x>=mod?mod:0;}
	inline void pt(int v){
		for(int i=1;i<=m*2;i++)if(v&(1<<(i-1)))pf("1");else pf("0");pf(" ");
	}
	inline short main(){
		file();
		n=read(),m=read();
		F(i,1,n)F(j,1,m)b[i][j]=read();
		if(n>m)memcpy(a,b,sizeof(a));
		else{
			std::swap(n,m);
			F(i,1,n)F(j,1,m)a[i][j]=b[m-j+1][i];
		}
		bin[0]=1;
		F(i,1,m)bin[i]=bin[i-1]<<2;
		f[0][0][nxt]=1;
		F(i,0,n-1)for(int j=0;j<(1<<(m<<1));j++)for(auto w:f[i][j]){
			auto zt=w.first;int v=w.second;
			for(int k=0;k<(1<<m);k++){
				int rate=1,cnt=0;
				for(int l=1;l<=m;l++)
					if(k&(1<<(l-1)))rate=1ll*rate*a[i+1][l]%mod,fa[l]=l;
					else rate=1ll*rate*(mod+1-a[i+1][l])%mod,fa[l]=0;
				if(!rate)continue;
				for(int l=1;l<=3;l++)if(zt.a[l])fa[l+m*2]=l+m*2,siz[l+m*2]=zt.a[l];
				for(int l=1;l<=m;l++){
					int b=(j>>((l-1)<<1))&3;
					if(!b)fa[l+m]=0;else fa[l+m]=b+m*2;
				}
				for(int l=1;l<=m;l++)if(k&(1<<(l-1)))fa[l]=l,siz[l]=1;else fa[l]=0;
				for(int l=1;l<=m;l++)if(fa[l]){
					if(l<m&&fa[l+1]&&find(l+1)!=find(l)){
						siz[find(l)]+=siz[find(l+1)];
						fa[find(l+1)]=find(l);
					}
					if(fa[l+m]&&find(l+m)!=find(l)){
						siz[find(l)]+=siz[find(l+m)];
						fa[find(l+m)]=find(l);
					}
				}nxt.a[1]=nxt.a[2]=nxt.a[3]=0,nxt.a[4]=zt.a[4];
				int s=0;
				for(int l=1;l<=m;l++)if(fa[l]){
					if(!vis[find(l)])
					vis[find(l)]=1,id[find(l)]=++cnt,nxt.a[cnt]=siz[find(l)],
					nxt.a[4]=max(nxt.a[4],nxt.a[cnt]),s|=cnt*bin[l-1];
					else s|=id[find(l)]*bin[l-1];
				}
				for(int l=1;l<=m;l++)if(fa[l])vis[find(l)]=0;
				add(f[i+1][s][nxt],1ll*v*rate%mod);
			}
		}
		int ans=0,tot=0;
		for(int i=0;i<(1<<(m<<1));i++)for(auto w:f[n][i])
			add(ans,1ll*w.first.a[4]*w.second%mod);
		pi(ans);
		return 0;
	}
}
signed main(){return EMT::main();}

高维游走

若每一维度走的是\(p_1,p_2,p_3...p_m\)则方案数是\(\binom{t_0}{p_1,p_2,p_3...p_m}\prod\limits_{i=1}^{m}\binom{t_i}{p_i}\)
若要使得其有1的贡献,根据卢卡斯定理,\(p_i\)\(t_i\)的子集,\((p_1,p_2,p_3...p_m,t_0-\sum\limits_{i=1}^{m}p_i)\)\(t_0\)的二进制划分。
那么设\(f_{i,j}\)表示dp了前i个二进制位,此时对于每一个不超过\(2^{i−1}\)的数\(v\)记录一个大小为\(m\)的布尔数组\(S_v\)\(S_{v,x} = 1\)表示\(2^ix+v\)的方案数为奇数,\(f_{i,j}\)表示\(S_v=j\)的数的数量。
考虑显然如果选择一个数\(k\)使得原式变成\(2^{(i+1)}\lfloor\frac{x+k}{2}\rfloor+v'\)
则当\(k+x\)是奇数时\(v'=v+2^i\)否则\(v'=v\),然后分这两种情况对所有\(k\)的合法集合进行异或,最后出来的状态就是要转移到的状态。

Code


#include<vector>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("travel.in","r",stdin);freopen("travel.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
	std::vector<int>in[35];ll f[35][1<<10];int st1[1<<10][35],st2[1<<10][35],m,t[35],num[2];
	inline void init(){
		int S=(1<<10)-1;
		for(int i=0;i<=S;i++)for(int j=0;j<=10;j++){
			for(int k=0;k<=9;k++)if(i&(1<<k)){
				int num=k+j;
				if(num&1)st2[i][j]|=1<<(num>>1);
				else st1[i][j]|=1<<(num>>1);
			}
		}
	}
	inline short main(){
		file();
		init();
		int T=read();
		while(T--){
			m=read();F(i,0,m)t[i]=read();
			memset(f,0,sizeof(f));num[0]=num[1]=0;
			F(i,0,31){
				in[i].clear();
				if(t[0]&(1<<i))F(j,1,m)if(t[j]&(1<<i))in[i].push_back(j);
				in[i].push_back(0);
			}
			for(auto i:in[0])num[i&1]^=(1<<(i>>1));
			f[1][num[0]]++,f[1][num[1]]++;
			F(i,2,31)for(int j=1;j<(1<<m);j++)if(f[i-1][j]){
				int s1=0,s2=0;
				for(auto k:in[i-1]){
					s1^=st1[j][k];
					s2^=st2[j][k];
				}f[i][s1]+=f[i-1][j],f[i][s2]+=f[i-1][j];
			}ll ans=0;
			for(int i=0;i<(1<<m);i++)ans+=1ll*__builtin_popcount(i)*f[31][i];
			pi(ans);pn();
		}
		return 0;
	}
}
signed main(){return EMT::main();}

过山车

暴力可以大力插头dp,但是我已经忘掉了,得抽个时间复习一下。
正解是网络流,把每个点看成两条边,分为两条横边/两条竖边/一个横边一个竖边,那么每个点的两条边都要有联通才能有解,于是跑最小费用最大流,跑出来的就是最少的损失,用总收益减去即可。
另外这题好像zkw跑得飞快,比ek快了好几倍。

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;typedef std::pair<int,int> pii;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("roller.in","r",stdin);freopen("roller.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=1e4+10,inf=0x3f3f3f3f;
	int co=1,head[N*10],col[200][50],S,T,n,m,id[200][50],cnt,w[200][50],H[200][50],L[200][50];bool able[200][50];
	struct node{int next,to,w1,w2;}e[N*100];
	inline void add(int next,int to,int w1,int w2){e[++co]={head[next],to,w1,w2},head[next]=co;}
	inline void ins(int next,int to,int w1,int w2){add(next,to,w1,w2),add(to,next,0,-w2);}
	namespace dinic{
		int dis[N*10];bool vis[N*10];
		inline bool spfa(){
			static int q[N*100],hd,tl;
			memset(dis,0x3f,sizeof(int)*(cnt+1));dis[S]=0;
			memset(vis,0,sizeof(bool)*(cnt+1));q[hd=tl=1]=S;
			while(hd<=tl){
				int x=q[hd++];vis[x]=0;
				for(int i=head[x],j;i;i=e[i].next)if(e[i].w1){
					if(dis[j=e[i].to]>dis[x]+e[i].w2){
						dis[j]=dis[x]+e[i].w2;
						if(!vis[j])vis[j]=1,q[++tl]=j;
					}
				}
			}return dis[T]!=inf;
		}
		inline int dfs(int x,int in){
			if(x==T)return in;
			int rest=in,go;vis[x]=1;
			for(int i=head[x],j;i;i=e[i].next)if(e[i].w1){
				if(dis[j=e[i].to]==dis[x]+e[i].w2&&!vis[j]){
					go=dfs(j,min(e[i].w1,rest));
					if(go)rest-=go,e[i].w1-=go,e[i^1].w1+=go;
					else dis[j]=-inf;
				}if(!rest)break;
			}return in-rest;
		}
		inline pii dinic(){
			pii ans(0,0);
			while(spfa()){
				int v=dfs(S,inf);
				ans.first+=v,ans.second+=dis[T]*v;
			}return ans;
		}
	}
	inline short main(){
		file();
		n=read(),m=read();S=++cnt,T=++cnt;
		F(i,1,n)F(j,1,m)able[i][j]=read()^1;
		F(i,1,n)F(j,1,m)w[i][j]=read(),id[i][j]=++cnt;
		F(i,1,n)F(j,1,m)col[i+1][j]=col[i][j]^1,col[i][j+1]=col[i][j]^1;
		int tot=0,sum=0;
		F(i,1,n)F(j,1,m)if(able[i][j]){
			tot++;sum+=w[i][j];
			H[i][j]=++cnt,L[i][j]=++cnt;
		}
		F(i,1,n)F(j,1,m)if(able[i][j]){
			if(col[i][j]){
				ins(id[i][j],H[i][j],1,0),
				ins(id[i][j],H[i][j],1,w[i][j]),
				ins(id[i][j],L[i][j],1,0),
				ins(id[i][j],L[i][j],1,w[i][j]),
				ins(S,id[i][j],2,0);
				if(able[i-1][j])ins(H[i][j],H[i-1][j],1,0);
				if(able[i+1][j])ins(H[i][j],H[i+1][j],1,0);
				if(able[i][j+1])ins(L[i][j],L[i][j+1],1,0);
				if(able[i][j-1])ins(L[i][j],L[i][j-1],1,0);
			}else{
				ins(H[i][j],id[i][j],1,0),
				ins(H[i][j],id[i][j],1,w[i][j]),
				ins(L[i][j],id[i][j],1,0),
				ins(L[i][j],id[i][j],1,w[i][j]),
				ins(id[i][j],T,2,0);
			}
		}
		pii ans=dinic::dinic();
		if(ans.first!=tot){puts("-1");return 0;}
		pi(sum-ans.second);
		return 0;
	}
}
signed main(){return EMT::main();}

木棍

把题目转化成前缀和的差分约束。
\(n\)个限制可以转化成对于一个区间\([l,r]\)包含了多少个这样的限制那么r的前缀和就比l的多多少个。
\(m\)个限制可以直接转换成\([l,r]\)中r的前缀和比l的多不超过c个。
然后再把关键点拿出来,每隔距离为k就多1的差(向上取整),然后搭配堆优化spfa和卡时就可以AC了。

Code




#include<queue>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("stick.in","r",stdin);freopen("stick.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=11000;
	int n,m,k,head[N],co,vis[N],dis[N],a[N],b[N],bk[N],x[N],y[N],yk[N],c[N],s[N],pre[6000][6000];bool in[N];
	struct node{int next,to,w;}e[N*3000];
	inline void add(int next,int to,int w){e[++co]={head[next],to,w},head[next]=co;}
	std::map<std::pair<int,int>,int>mp;
	struct dp{int x,v;friend bool operator <(dp a,dp b){return a.v>b.v;}};
	inline void spfa(){
		memset(dis,0x3f,sizeof(dis));
		std::priority_queue<dp>q;
		dis[s[0]+1]=0;q.push({s[0]+1,0});while(!q.empty()){
			if(1.0*clock()/CLOCKS_PER_SEC>=1.9){puts("No");exit(0);}
			int x=q.top().x;q.pop();vis[x]++;in[x]=0;if(vis[x]>s[0]){puts("No");exit(0);}
			for(int i=head[x],j;i;i=e[i].next)if(dis[j=e[i].to]>dis[x]+e[i].w){
				dis[j]=dis[x]+e[i].w;if(!in[j])in[j]=1,q.push({j,dis[j]});
			}
		}
	}
	inline void init(int &x){x=std::lower_bound(s+1,s+s[0]+1,x)-s;}
	inline int get(int a,int x,int b,int y){return pre[x][y]-pre[a-1][y]-pre[x][b-1]+pre[a-1][b-1];}
	inline short main(){
		file();
		n=read(),m=read(),k=read();
		F(i,1,n)a[i]=read()+2,b[i]=read()+2,bk[i]=b[i]-k,s[++s[0]]=a[i]-1,s[++s[0]]=bk[i],a[i]--;
		F(i,1,m)x[i]=read()+2,y[i]=read()+2,yk[i]=y[i]-k,c[i]=read(),s[++s[0]]=x[i]-1,s[++s[0]]=yk[i],x[i]--;
		std::sort(s+1,s+s[0]+1);s[0]=std::unique(s+1,s+s[0]+1)-s-1;
		F(i,1,n)init(a[i]),init(bk[i]),add(bk[i],a[i],-1),pre[a[i]][bk[i]]++;
		F(i,1,m)init(x[i]),init(yk[i]),add(x[i],yk[i],c[i]);
		F(i,1,s[0])F(j,1,s[0])pre[i][j]=pre[i][j]+pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1];
		F(i,1,s[0])F(j,i+1,s[0]){int cnt=get(i,s[0],1,j);add(j,i,-cnt);add(i,j,(s[j]-s[i])%k==0?(s[j]-s[i])/k:(s[j]-s[i])/k+1);}
		F(i,1,s[0])add(s[0]+1,i,0);
		spfa();puts("Yes");
		return 0;
	}
}
signed main(){return EMT::main();}

这组题人造的什么水数据啊,痛斥组题人

End Sky II

依然是按位考虑,先求出每个最大值控制区间然后用线段树转移即可,不知道为啥跑的飞快,可能是组题人造的数据太水了

Code


#include<vector>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("sky.in","r",stdin);freopen("sky.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=2e5+10;
	int n,k,a[N],l[N],r[N],s[N],top;std::vector<int>nowv,nxtv;bool fl;
	struct seg{
		int lz[N<<2];
		inline void clear(){memset(lz,-0x3f,sizeof(int)*(n<<2));}
		inline void change(int p,int l,int r,int ql,int qr,int v){
			if(l>=ql&&r<=qr){lz[p]=max(lz[p],v);return;}
			int mid=(l+r)>>1;
			if(qr<=mid)change(p<<1,l,mid,ql,qr,v);
			else if(ql>mid)change(p<<1|1,mid+1,r,ql,qr,v);
			else change(p<<1,l,mid,ql,mid,v),change(p<<1|1,mid+1,r,mid+1,qr,v);
		}
		inline int ask(int p,int l,int r,int x){
			if(l==r)return lz[p];
			int mid=(l+r)>>1;
			if(x<=mid)return max(lz[p],ask(p<<1,l,mid,x));
			else return max(lz[p],ask(p<<1|1,mid+1,r,x));
		}
	}segm;
	inline bool check(int bit){
		if(fl)nowv.swap(nxtv);nxtv.clear();
		int cnt=0,ans=0;
		for(auto x:nowv)if(a[x]&(1<<bit))cnt++;
		if(cnt<k)return fl=0;
		segm.clear();
		segm.change(1,1,n,1,1,0);
		for(auto x:nowv)if(a[x]&(1<<bit)){
			int val=segm.ask(1,1,n,l[x])+1;
			segm.change(1,1,n,1,r[x],val);
			if(r[x]==n)ans=max(ans,val);
		}
		if(ans>=k){
			for(auto x:nowv)if(a[x]&(1<<bit))nxtv.push_back(x);
			return fl=1;
		}else return fl=0;
	}
	inline short main(){
		file();
		n=read(),k=read();
		F(i,1,n)a[i]=read(),nowv.push_back(i);
		F(i,1,n){
			while(top&&a[s[top]]<a[i])r[s[top--]]=i-1;
			l[i]=s[top]+1,s[++top]=i;
		}
		while(top)r[s[top--]]=n;
		int ans=0;
		D(i,30,0)if(check(i))ans|=1<<i;
		pi(ans);
		return 0;
	}
}
signed main(){return EMT::main();}

Hill of Sunflowers

n范围很小所以可以直接暴搜出相对大小关系,然后dp一下前i段用了j个区间方案数即可。

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("hill.in","r",stdin);freopen("hill.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int mod=1e9+7;
	int n,a[15],b[15],inv[15],ans;
	inline int C(int n,int m){if(n<0||m<0||n<m)return 0;int ans=1;F(i,1,m)ans=1ll*ans*(n-i+1)%mod*inv[i]%mod;return ans;}
	inline int getlen(){
		static int f[15];
		int ans=0;
		F(i,1,n){
			f[i]=1;
			F(j,1,i-1)if(b[j]<b[i])f[i]=max(f[i],f[j]+1);
			ans=max(ans,f[i]);
		}return ans;
	}
	inline int ksm(int a,int b){
		int ans=1;
		while(b){
			if(b&1)ans=1ll*a*ans%mod;
			a=1ll*a*a%mod;b>>=1;
		}return ans;
	}
	inline void check(int lim){
		static int c[15],f[15][15],up[15];
		int len=getlen();
		F(i,1,lim){bool fl=0;F(j,1,n)if(b[j]==i){fl=1;break;}if(!fl)return;}
		F(i,1,lim)up[i]=1e9+7;
		F(i,1,n)up[b[i]]=min(up[b[i]],a[i]);
		memset(f,0,sizeof(f));
		F(i,1,lim)c[i]=up[i];std::sort(c+1,c+lim+1);int L=std::unique(c+1,c+lim+1)-c-1;
		F(i,1,lim)up[i]=std::lower_bound(c+1,c+L+1,up[i])-c;
		memset(f,0,sizeof(f));
		f[0][0]=1;
		F(i,1,lim){
			F(j,1,L){
				int mn=1e9+7;
				D(k,i-1,0){
					mn=min(mn,up[k+1]);
					if(mn<j)break;
					F(l,0,j-1){
						f[i][j]+=1ll*f[k][l]*C(c[j]-c[j-1],i-k)%mod;
						f[i][j]-=f[i][j]>=mod?mod:0;
					}
				}
			}
		}int sum=0;
		F(i,1,L)sum+=f[lim][i],sum-=sum>=mod?mod:0;
		ans+=1ll*sum*len%mod,ans-=ans>=mod?mod:0;
	}
	inline void dfs(int x,int lim){
		if(x==n+1){check(lim);return;}
		F(i,1,lim)b[x]=i,dfs(x+1,lim);
	}
	inline short main(){
		file();
		n=read();
		F(i,1,n)a[i]=read();inv[0]=inv[1]=1;F(i,2,n)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
		F(i,1,n)dfs(1,i);
		F(i,1,n)ans=1ll*ans*ksm(a[i],mod-2)%mod;
		pi(ans);
		return 0;
	}
}
signed main(){return EMT::main();}

Wonderful Everyday

暴力\(nm\)水了一堆AC,再次痛斥组题人造的什么水数据
然后交了一发hack数据结果重新评测的时候是倒着评的然后明明A掉了结果算的是第一次没A的代码,淦
由于数位上没有0所以进位一定是\((m-1)+(m-1)\)或者\(m+(1...m)\)然后哈希判一下即可。

Code


#include<cassert>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;typedef unsigned long long ull;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("wonderful.in","r",stdin);freopen("wonderful.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=2e5+10;const ll mod=998244354;
	char s[N],t[N];ull hs1[N],hs2[N],p[N];
	int n,m,same[N];ll hs3[N],hs4[N],p2[N];
	inline ull gethash(int l,int r,ull *hash){return hash[r]-hash[l-1]*p[r-l+1];}
	inline ll gethash(int l,int r,ll *hash){return (hash[r]-hash[l-1]*p2[r-l+1]%mod+mod)%mod;}
	inline int get(int st){
		int l=1,r=m-1,ans=0;
		ull base=gethash(st,st+m-1,hs1);ll base2=gethash(st,st+m-1,hs3);
		while(l<=r){
			int mid=(l+r)>>1;
			if(base-gethash(1,mid,hs2)*p[m-mid]==gethash(st+mid,st+m-1,hs1)&&(base2-gethash(1,mid,hs4)*p2[m-mid]%mod+mod)%mod==gethash(st+mid,st+m-1,hs3))l=mid+1,ans=mid;
			else r=mid-1;
		}return ans;
	}
	inline bool check1(){
		F(i,1,n-m){
			int l1=i,r1=i+m-1,l2=i+m,len2=r1-l1+1-same[i],r2=l2+len2-1;
			if(r2<=n&&r2>=l2){
				if(gethash(l1,r1,hs1)+gethash(l2,r2,hs1)==hs2[m]&&(gethash(l1,r1,hs3)+gethash(l2,r2,hs3))%mod==hs4[m]){
					pi(l1);pi(r1);pn();pi(l2);pi(r2);pn();return 1;
				}
			}r2--;
			if(r2<=n&&r2>=l2){
				if(gethash(l1,r1,hs1)+gethash(l2,r2,hs1)==hs2[m]&&(gethash(l1,r1,hs3)+gethash(l2,r2,hs3))%mod==hs4[m]){
					pi(l1);pi(r1);pn();pi(l2);pi(r2);pn();return 1;
				}
			}
		}return 0;
	}
	inline bool check2(){
		F(i,2,n-m+1){
			int l1,r1=i-1,l2=i,r2=i+m-1,len1=r2-l2+1-same[i];l1=r1-len1+1;
			if(l1>0&&l1<=r1){
				if(gethash(l1,r1,hs1)+gethash(l2,r2,hs1)==hs2[m]&&(gethash(l1,r1,hs3)+gethash(l2,r2,hs3))%mod==hs4[m]){
					pi(l1);pi(r1);pn();pi(l2);pi(r2);pn();return 1;
				}
			}l1++;
			if(l1>0&&l1<=r1){
				if(gethash(l1,r1,hs1)+gethash(l2,r2,hs1)==hs2[m]&&(gethash(l1,r1,hs3)+gethash(l2,r2,hs3))%mod==hs4[m]){
					pi(l1);pi(r1);pn();pi(l2);pi(r2);pn();return 1;
				}
			}
		}return 0;
	}
	inline bool check3(){
		F(i,m-1,n-m+1){
			int l1=i-m+2,r1=i,l2=i+1,r2=l2+m-2;
			if(l1>0&&l1<=r1){
				if(gethash(l1,r1,hs1)+gethash(l2,r2,hs1)==hs2[m]&&(gethash(l1,r1,hs3)+gethash(l2,r2,hs3))%mod==hs4[m]){
					pi(l1);pi(r1);pn();pi(l2);pi(r2);pn();return 1;
				}
			}
		}return 0;
	}
	inline short main(){
		file();
		scanf("%s%s",s+1,t+1);p[0]=p2[0]=1;n=strlen(s+1),m=strlen(t+1);
		int mx=max(n,m);F(i,1,mx)p[i]=p[i-1]*10,p2[i]=p2[i-1]*10%mod;
		F(i,1,n)hs1[i]=hs1[i-1]*10+s[i]-'0',hs3[i]=(hs3[i-1]*10+s[i]-'0')%mod;
		F(i,1,m)hs2[i]=hs2[i-1]*10+t[i]-'0',hs4[i]=(hs4[i-1]*10+t[i]-'0')%mod;
		F(i,1,n-m+1)same[i]=get(i);
		if(check1())return 0;
		if(check2())return 0;
		if(check3())return 0;
		pi(-1);
		return 0;
	}
}
signed main(){return EMT::main();}

排队

考场上使用的\(O(1s)\)做法:
反复用BIT做最长上升子序列,然后更新树状数组时如果当前权值和现在的v相等可以随机是谁覆盖谁,那么如果一个数在每次都被使用就是答案。

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<random>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	std::mt19937 rnd((unsigned long long)(new int));
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("queue.in","r",stdin);freopen("queue.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=1e5+10;
	int vis[N],tim,n,a[N],pre[N];
	bool fl,now=1;
	struct Bit{
		struct dp{int x,v;}t[N];
		inline void clear(){memset(t,0,sizeof(dp)*(n+1));}
		inline void add(int x,dp v){while(x<=n){if(t[x].v<v.v)t[x]=v;else if(t[x].v==v.v&&(rnd()&1))t[x]=v;x+=x&-x;}}
		inline dp ask(int x){dp ans={0,0};while(x){if(t[x].v>ans.v)ans=t[x];else if(t[x].v==ans.v&&(rnd()&1))ans=t[x];x-=x&-x;}return ans;}
	}bit;
	struct Bit2{
		struct dp{int x,v;}t[N];
		inline void clear(){memset(t,0,sizeof(dp)*(n+1));}
		inline void add(int x,dp v){while(x){if(t[x].v<v.v)t[x]=v;else if(t[x].v==v.v&&(rnd()&1))t[x]=v;x-=x&-x;}}
		inline dp ask(int x){dp ans={0,0};while(x<=n){if(t[x].v>ans.v)ans=t[x];else if(t[x].v==ans.v&&(rnd()&1))ans=t[x];x+=x&-x;}return ans;}
	}bit2;
	inline void solve(){
		if(now){
			bit.clear();
			F(i,1,n){auto v=bit.ask(a[i]);pre[i]=v.x;bit.add(a[i],{i,v.v+1});}
			auto v=bit.ask(n);if(!fl)pi(v.v),pn(),fl=1;
			for(int i=v.x;i;i=pre[i])vis[i]++;
		}else{
			bit2.clear();
			D(i,n,1){auto v=bit2.ask(a[i]);pre[i]=v.x;bit2.add(a[i],{i,v.v+1});}
			auto v=bit2.ask(1);
			for(int i=v.x;i;i=pre[i])vis[i]++;
		}
	}
	inline short main(){
		file();
		n=read();
		F(i,1,n)a[i]=read();
		while(1.0*clock()/CLOCKS_PER_SEC<=0.95){tim++;solve();now^=1;}
		F(i,1,n)if(vis[i]==tim)pi(i);
		return 0;
	}
}
signed main(){return EMT::main();}

树论

\(f_{x,i}\)表示x点选数为i的方案数,那么有:

\[f_{x,i}=\prod\limits_{y\in son_x}(\sum\limits_{[l_y\leq j \leq r_y][gcd(i,j)=1]}f_{y,j}) \]

\[=\prod\limits_{y\in son_x}{\sum\limits_{d|i}\mu(d)\sum\limits_{d|j}f_{y,j}} \]

那么就可以\(nw\ln w\)求出一个点的值,然后换根dp即可。

Code
#include<vector>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("tree.in","r",stdin);freopen("tree.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int mod=1e9+7;
	int id[55],fa[55],f[55][51000],tmp[51000],g[55][51000],prime[51000],co,mu[51000],n,l[55],r[55],ans[55];bool vis[51000];
	std::vector<int>w[55];
	inline void add(int &x,int v){x+=v,x-=x>=mod?mod:0;}
	inline void init(int n){
		mu[1]=1;
		F(i,2,n){
			if(!vis[i])prime[++co]=i,mu[i]=mod-1;
			F(j,1,co){
				if(i*prime[j]>n)break;
				vis[i*prime[j]]=1;
				if(i%prime[j]==0)break;
				if(mu[i])mu[i*prime[j]]=mod-mu[i];
			}
		}
	}
	inline void dfs1(int x){
		int cnt=0;
		for(auto y:w[x])if(y!=fa[x])fa[y]=x,dfs1(y),id[y]=++cnt;
		F(i,l[x],r[x])f[x][i]=1;
		for(auto y:w[x])if(y!=fa[x]){
			memset(tmp,0,sizeof(tmp));
			F(d,1,50000)if(mu[d]){
				int sum=0,val;
				for(int k=d;k<=50000;k+=d)add(sum,f[y][k]);val=1ll*sum*mu[d]%mod;
				for(int j=d;j<=50000;j+=d)add(tmp[j],val);
			}
			F(i,l[x],r[x])f[x][i]=1ll*f[x][i]*tmp[i]%mod;
		}
	}
	inline void dfs2(int x){
		static int pre[55][51000],suf[55][51000];
		if(x!=1)F(i,l[x],r[x])add(ans[x],1ll*i*g[x][i]%mod*f[x][i]%mod);
		if(w[x].size()==1&&x!=1)return;
		F(i,l[x],r[x])pre[0][i]=suf[x==1?w[x].size()+1:w[x].size()][i]=1;
		for(auto y:w[x])if(y!=fa[x]){
			memset(tmp,0,sizeof(tmp));
			F(d,1,50000)if(mu[d]){
				int sum=0,val;
				for(int k=d;k<=50000;k+=d)add(sum,f[y][k]);val=1ll*sum*mu[d]%mod;
				for(int j=d;j<=50000;j+=d)add(tmp[j],val);
			}
			F(i,l[x],r[x])pre[id[y]][i]=1ll*pre[id[y]-1][i]*tmp[i]%mod;
		}
		for(int kk=w[x].size()-1;kk>=0;kk--){
			int y=w[x][kk];if(y==fa[x])continue;
			memset(tmp,0,sizeof(tmp));
			F(d,1,50000)if(mu[d]){
				int sum=0,val;
				for(int k=d;k<=50000;k+=d)add(sum,f[y][k]);val=1ll*sum*mu[d]%mod;
				for(int j=d;j<=50000;j+=d)add(tmp[j],val);
			}
			F(i,l[x],r[x])suf[id[y]][i]=1ll*suf[id[y]+1][i]*tmp[i]%mod;
		}
		for(auto y:w[x])if(y!=fa[x]){
			F(d,1,50000)if(mu[d]){
				int sum=0,val;
				for(int k=d;k<=50000;k+=d)add(sum,1ll*pre[id[y]-1][k]*suf[id[y]+1][k]%mod*g[x][k]%mod);
				val=1ll*sum*mu[d]%mod;
				for(int j=d;j<=50000;j+=d)if(j>=l[y]&&j<=r[y])add(g[y][j],val);
			}
		}
		for(auto y:w[x])if(y!=fa[x])dfs2(y);
	}
	inline short main(){
		file();
		init(50000);
		n=read();
		F(i,1,n)l[i]=read();
		F(i,1,n)r[i]=read();
		F(i,2,n){
			int x=read(),y=read();
			w[x].push_back(y);
			w[y].push_back(x);
		}dfs1(1);
		F(i,l[1],r[1])add(ans[1],1ll*i*f[1][i]%mod),g[1][i]=1;
		dfs2(1);
		F(i,1,n)pi(ans[i]),pn();
		return 0;
	}
}
signed main(){return EMT::main();}

麻烦的杂货店

考虑维护出一个区间[L,R]最左边的前缀最小值所在位置\(l\)和最右边的前缀最小值所在位置\(r\),那么\(r-l\)可以贡献给答案。
然后维护出\([L,l)\)中每个点向右的最长合法长度和\((r,R]\)向左的最长合法长度,三个取max即可。
于是做到了预处理\(nlog_n\)回答\(O(1)\)

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	#define getchar getchar_unlocked
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("grocery.in","r",stdin);freopen("grocery.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=1e5+10;
	int n,m,a[N],stl[20][N],str[20][N],stu[20][N],stv[20][N],log[N],pre[N],lst[N],nxt[N],plc[N<<1];
	inline int pmin(int x,int y){return pre[x]<=pre[y]?x:y;}inline int id(int x){return x+n;}
	inline void init(){
		F(i,1,n)pre[i]=pre[i-1]+a[i];
		F(i,1,n)stl[0][i]=str[0][i]=i;
		memset(lst,-1,sizeof(int)*(n+1));
		memset(nxt,-1,sizeof(int)*(n+1));
		memset(plc,-1,sizeof(int)*(n*2+1));
		plc[id(0)]=0;
		F(i,1,n){
			if(pre[i]<pre[i-1])plc[id(pre[i-1])]=-1;
			if(~plc[id(pre[i])])nxt[lst[i]=plc[id(pre[i])]]=i;
			plc[id(pre[i])]=i;
		}
		D(i,n,0){
			if(~nxt[i]&&~nxt[nxt[i]])nxt[i]=nxt[nxt[i]];
			stu[0][i]=~nxt[i]?nxt[i]-i:0;
		}
		F(i,0,n){
			if(~lst[i]&&~lst[lst[i]])lst[i]=lst[lst[i]];
			stv[0][i]=~lst[i]?i-lst[i]:0;
		}
	}
	inline void rmq_init(){
		F(j,1,log[n])F(i,0,n){
			stl[j][i]=stl[j-1][i];
			str[j][i]=str[j-1][i];
			stu[j][i]=stu[j-1][i];
			stv[j][i]=stv[j-1][i];
			int k=i-(1<<(j-1));
			if(k>=0){
				stl[j][i]=pmin(stl[j-1][k],stl[j][i]);
				str[j][i]=pmin(str[j][i],str[j-1][k]);
				stu[j][i]=max(stu[j][i],stu[j-1][k]);
				stv[j][i]=max(stv[j][i],stv[j-1][k]);
			}
		}
	}
	inline short main(){
		file();
		n=read(),m=read();
		log[0]=-1;F(i,1,n+1)log[i]=log[i>>1]+1;
		F(i,1,n){
			char ch=getchar();
			while(ch!='F'&&ch!='T')ch=getchar();
			a[i]=ch=='F'?1:-1;
		}init();rmq_init();
		while(m--){
			int l=read()-1,r=read(),len=r-l+1,lg=log[len];
			int L=pmin(stl[lg][l+(1<<lg)-1],stl[lg][r]);
			int R=pmin(str[lg][r],str[lg][l+(1<<lg)-1]);
			int ans=R-L;
			if(l<L){
				L--;len=L-l+1;lg=log[len];
				ans=max(ans,max(stu[lg][l+(1<<lg)-1],stu[lg][L]));
			}
			if(r>R){
				R++;len=r-R+1;lg=log[len];
				ans=max(ans,max(stv[lg][R+(1<<lg)-1],stv[lg][r]));
			}pi(ans);pn();
		}
		return 0;
	}
}
signed main(){return EMT::main();}

发现当k和n不互质时无解,证明如下:
\(s_i\)中所有1的下标之和为\(w_i\)
那么一操作就是\(w_{i+1}\equiv w_i+x*k (\bmod n)\)
二操作就是\(w_{i+1}\equiv w_i+1(\bmod n)\)
说明\(x*k \equiv 1(\bmod n)\)要成立,那么k只能有逆元了,显然要和n互质.
那么开始构造.
我们当第i轮时把1放在\(\frac{i}{k},\frac{i+1}{k}...\frac{i+k-1}{k}\)处,
那么显然我们可以通过右移\(\frac{1}{k}\)位实现一操作,然后考虑最后一项\(\frac{i+k-1}{k}+1=\frac{(i+1)+k-1}{k}\)可以实现二操作,那么方案就合法了。

Code


#include<bitset>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("circle.in","r",stdin);freopen("circle.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	int n,k,l;
	inline int gcd(int a,int b){return b?gcd(b,a%b):a;}
	inline void exgcd(int a,int b,int &x,int &y){if(!b){x=1,y=0;return;}exgcd(b,a%b,x,y);int z=x;x=y,y=z-a/b*y;}
	inline void pt(std::bitset<110> &v){for(int i=0;i<n;i++)putchar(v[i]+'0');pn();}
	inline short main(){
		file();
		int T=read();
		while(T--){
			n=read(),k=read(),l=read();
			if(gcd(n,k)!=1){puts("NO");continue;}
			int inv,ee;
			exgcd(k,n,inv,ee);inv=(inv+n)%n;
			puts("YES");
			std::bitset<110>now;
			F(i,0,k-1)now[1ll*i*inv%n]=1;
			for(int i=0;i<l-1;i++){
				pt(now);
				now[1ll*i*inv%n]=0;
				now[(1ll*i*inv%n+1)%n]=1;
			}pt(now);
		}
		return 0;
	}
}
signed main(){return EMT::main();}

DNA序列

首先,每个字符串后面加一个很大的字符(例如“U”)。
对于每个字符串\(S_i\),找到最短的前缀\(X\)满足\(X^{\infty}<S_i\)
然后把后面连续的几段拿出来变成\(S_i=x^k+Z\)
那么我们可以先按照x的字典序排序,之后由于要用到后缀所以要把后缀从大到小排序。
知道顺序之后,dp一下就行了。

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using std::string;
namespace EMT{
	typedef long long ll;typedef double db;typedef string str;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("dna.in","r",stdin);freopen("dna.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=55;
	str s[N];int n,len[N];
	inline int gcd(int a,int b){return b?gcd(b,a%b):a;}
	inline int lcm(int a,int b){return a*b/gcd(a,b);}
	inline int cmp(str a,str b){//a<b
		int alen=a.size(),blen=b.size(),lc=lcm(alen,blen);
		str aa,bb;F(i,1,lc/alen)aa+=a;F(i,1,lc/blen)bb+=b;
		if(aa<bb)return 1;
		if(aa==bb)return 0;
		if(aa>bb)return -1;
	}
	struct point{str x,z;friend bool operator <(point a,point b){int v=cmp(a.x,b.x);if(v==1)return 1;if(v==-1)return 0;return a.z>b.z;}}p[N];
	int a[N];str ans;bool fl;
	inline void solve(){
		static bool vis[55][2505];static str f[55][2505];
		vis[0][0]=1;int sum=0;
		for(int i=0;i<n;i++){
			F(j,0,sum)if(vis[i][j]){
				str now=f[i][j];int siz=j;
				for(int l=0;l<len[a[i+1]];l++){
					now+=s[a[i+1]][l];siz++;
					if(!vis[i+1][siz])vis[i+1][siz]=1,f[i+1][siz]=now;
					else if(f[i+1][siz]>now)f[i+1][siz]=now;
				}
			}
			sum+=len[a[i+1]];
		}
		F(i,0,sum)if(vis[n][i]){
			if(!fl)ans=f[n][i],fl=1;
			else if(ans>f[n][i])ans=f[n][i];
		}std::cout<<ans;
	}
	inline short main(){
		file();
		n=read();
		F(i,1,n)std::cin>>s[i],s[i]+="Z",len[i]=s[i].size();
		F(i,1,n){
			str now;
			F(j,0,len[i]-1){
				now+=s[i][j];str base;
				while(base.size()<s[i].size())base+=now;
				if(base<s[i]){
					p[i].x=now;
					int key=j+1,cnt=1;
					while(key+j<len[i]){
						bool fl=1;
						F(k,0,j)if(s[i][k+key]!=now[k]){fl=0;break;}
						if(!fl)break;
						cnt++;key+=j+1;
					}
					F(k,key,len[i]-1)p[i].z+=s[i][k];
					break;
				}
			}
		}F(i,1,n)a[i]=i;
		std::sort(a+1,a+n+1,[](int i,int j){return p[i]<p[j];});
		solve();
		return 0;
	}
}
signed main(){return EMT::main();}

探寻

考虑用一个set维护当前有正收益的点
如果这个点的祖先已经访问过了,可以直接把这个点买下来,
否则把这个点并到祖先身上,用并查集维护一下即可。

Code


#include<set>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("prospecting.in","r",stdin);freopen("prospecting.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
	const int N=2e5+10;
	bool vis[N];
	namespace dsu{
		int fa[N];
		inline void init(int n){F(i,0,n)fa[i]=i;}
		inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
		inline void merge(int x,int y){fa[find(x)]=find(y);}
	}
	int n,fa[N];ll x[N],y[N],ans,now;
	struct dp{ll x,y;int id;friend bool operator <(dp a,dp b){return ((a.y<a.x)^(b.y<b.x))?a.y<a.x:(a.y==b.y?a.id<b.id:a.y<b.y);}};
	std::set<dp>s;
	inline short main(){
		file();
		n=read();
		F(i,1,n-1)fa[i]=read(),x[i]=read(),y[i]=read(),x[i]=x[i]==-1?1e18:x[i],s.insert({x[i],y[i],i});
		dsu::init(n-1);vis[0]=1;
		while(s.size()){
			auto v=*s.begin();s.erase(s.begin());
			vis[v.id]=1;dsu::fa[v.id]=fa[v.id];
			if(!vis[dsu::find(v.id)]){
				int Fa=dsu::find(v.id);
				s.erase(s.find({x[Fa],y[Fa],Fa}));
				if(x[Fa]<v.y){
					y[Fa]+=v.y-x[Fa];
					x[Fa]=v.x;
					s.insert({x[Fa],y[Fa],Fa});
				}else{
					x[Fa]+=v.x-v.y;
					s.insert({x[Fa],y[Fa],Fa});
				}
			}else{
				if(now<v.y)ans+=v.y-now,now=v.y;
				now+=v.x-v.y;
			}
		}pi(ans);
		return 0;
	}
}
signed main(){return EMT::main();}

进制转换

八十分的话就是考虑b进制表示下的位数<=5时可以\(10^{n-1}\)求解,否则直接枚举符合条件的数。
正解的话其实差不多,枚举第m项的系数和m是几(m表示最大次数),然后有显然的柿子是

\[a_mb^m<=y<(a_m+1)b^m \]

然后考虑a在[0,9]范围内,所以

\[y<a_m*b_m+9\times \frac{b^m-1}{b-1} \]

然后可以开根就有b的范围,很小,可以暴力枚举。

Code
#include<vector>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	#define int long long
	inline ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("number.in","r",stdin);freopen("number.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
	ll y,l;
	std::vector<int>bin,w;
	inline ll get(ll x){return x>10?x:10;}
	inline bool check(ll b){
		ll now=y;
		w.clear();
		while(now){
			int x=now%b;
			if(x>=10)return 0;
			w.push_back(x);
			now/=b;
		}
		if(w.size()<bin.size())return 0;
		if(w.size()>bin.size())return 1;
		for(int i=w.size()-1;i>=0;i--)
		if(w[i]>bin[i])return 1;
		else if(w[i]<bin[i])return 0;
		return 1;
	}
	inline short main(){
		file();
		int T=read();
		while(T--){
			y=read(),l=read();
			bin.clear();ll now=l;
			while(now)bin.push_back(now%10),now/=10;
			bool fl=0;
			for(int m=bin.size()-1;m<9;m++){
				for(int a=1;a<=9;a++){
					ll r=powl(1.0L*y/a,1.0L/m),l=get(powl(1.0L*(y-9.0L*(powl(r,m)-1)/(r-1))/a,1.0L/m));
					for(ll b=r;b>=l;b--)if(check(b)){fl=1;pi(b);pn();break;}
					if(fl)break;
				}if(fl)break;}
			if(fl)continue;
			D(b,100,10)if(check(b)){pi(b);pn();break;}
		}
		return 0;
	}
}
signed main(){return EMT::main();}

遇到困难睡大觉

缝合SA即可。
首先退火出一个较优解方法是交换一个值和另一个最值。
然后退火出最小值应该是什么,然后求解即可。

Code


#include<queue>
#include<set>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("sleep.in","r",stdin);freopen("sleep.out","w",stdout);}
	inline ll max(ll a,ll b){return a>b?a:b;}inline ll min(ll a,ll b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=1e5+10;const db alp=0.997;const ll inf=1e18;
	struct dp{int x;ll val;friend bool operator <(dp a,dp b){return a.val==b.val?a.x<b.x:a.val<b.val;}};
	std::multiset<dp>s1,s2;
	int a[N],b[N],n,k,p[N];ll ans,ansx;
	inline void del(int i){s1.erase(s1.find({i,a[p[i]]+1ll*i*k})),s2.erase(s2.find({i,b[p[i]]+1ll*i*k}));}
	inline void ins(int i){s1.insert({i,a[p[i]]+1ll*i*k}),s2.insert({i,b[p[i]]+1ll*i*k});}
	inline int random(int x){return 1ll*rand()*rand()%x;}
	inline void SA(){
		db T=3000;
		while(T>1e-15){
			int x=random(n)+1,y=rand()&1?s2.rbegin()->x:s1.begin()->x;
			if(x==y){T*=alp;continue;}
			del(x),del(y),
			std::swap(p[x],p[y]);
			ins(x),ins(y);
			ll now=s1.begin()->val-s2.rbegin()->val,deta=now-ans;
			if(deta>0){
				ans=now;
			}else if(exp(deta/T)*RAND_MAX<=rand())
			del(x),del(y),std::swap(p[x],p[y]),ins(x),ins(y);
			T*=alp;
		}
	}
	struct pt{int a,b;friend bool operator <(pt a,pt b){return a.b==b.b?a.a<b.a:a.b<b.b;}};
	inline ll clac(ll v){
		std::priority_queue<pt>q;
		int pos=1;ll mx=0,mn=1e18;
		F(i,1,n){
			while(pos<=n&&1ll*i*k+a[p[pos]]>=v)q.push({a[p[pos]],b[p[pos]]}),pos++;
			if(!q.size())return -inf;
			mx=max(mx,1ll*i*k+q.top().b);
			mn=min(mn,1ll*i*k+q.top().a);
			q.pop();
		}return mn-mx;
	}
	inline int random(int l,int r){return l+random(r-l+1);}
	inline void SA2(){
		db T=1e9,x=ansx;
		while(T>1&&1.0*clock()/CLOCKS_PER_SEC<=2.85){
			db X=random(-T/2,T);
			ll now=clac(X+x),deta=now-ans;
			if(now==-inf){T*=0.98;continue;}
			if(deta>0){
				x=ansx=x+X,ans=now;
			}else if(exp(deta/T)*RAND_MAX>=rand())x+=X;
			T*=0.98;
		}
	}
	inline short main(){
        file();
		srand((unsigned long long)(new int));
		n=read(),k=read();
		F(i,1,n)b[i]=read(),a[i]=read();
		F(i,1,n)p[i]=i;
		std::random_shuffle(p+1,p+n+1);
		F(i,1,n)ins(i);
		ans=s1.begin()->val-s2.rbegin()->val;
		while(1.0*clock()/CLOCKS_PER_SEC<=1.5)SA();
		ansx=s1.begin()->val;
		std::sort(p+1,p+n+1,[](int i,int j){return a[i]>a[j];});
		while(1.0*clock()/CLOCKS_PER_SEC<=2.85)SA2();
		pi(ans);
		return 0;
	}
}
signed main(){return EMT::main();}

張士超你昨天晚上到底把我家鑰匙放在哪了

考虑dp,设\(f_{i,j,k,t}\)表示前i个地方,钦定超额的\(1-p\)\(a_i\)总和为j,\(p\)\(a_i\)总和为\(k\),现在找到了t个地方的方案*概率。
转移就是枚举这次有没有找到,钦不钦定,然后如果钦定带上\(-1\)的容斥系数。
最后求答案就是枚举\(M,j,k,t\)考虑还需要找到多少把钥匙,然后钦定找到了\(k\)个地方,然后是两个插板法相乘即可。

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("key.in","r",stdin);freopen("key.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int mod=998244353;
	int f[2][110][110][110],M,d,N,n,a[110],p[110],jc[1100],inv[1100],C1[110][110],C2[110][110];
	inline int ksm(int a,int b){
		int ans=1;
		while(b){
			if(b&1)ans=1ll*a*ans%mod;
			a=1ll*a*a%mod;b>>=1;
		}return ans;
	}
	inline void add(int &x,int v){x+=v,x-=x>=mod?mod:0;}
	inline int C(int n,int m){
		if(n<m||m<0)return 0;int ans=inv[m];
		for(int i=n;i>n-m;i--)ans=1ll*ans*i%mod;
		return ans;
	}
	inline short main(){
		file();
		M=read(),d=read(),N=read(),n=read();
		f[0][0][0][0]=1;
		jc[0]=inv[0]=inv[1]=1;
		F(i,1,1000)jc[i]=1ll*jc[i-1]*i%mod;
		inv[1000]=ksm(jc[1000],mod-2);
		D(i,999,2)inv[i]=1ll*(i+1)*inv[i+1]%mod;
		F(i,0,N/d)F(j,0,M)C1[i][j]=C(n-1-i*d+j,j),C2[i][j]=C(N-n-i*d+j,j);
		F(i,1,M)a[i]=(read()+1)/d,p[i]=read();
		for(int i=0;i<M;i++){
			int now=i&1,nxt=now^1;
			memset(f[nxt],0,sizeof(f[nxt]));
			F(j,0,N/d)F(k,0,N/d)F(t,0,i)if(f[now][j][k][t]){
				if(j+a[i+1]<=N/d)add(f[nxt][j+a[i+1]][k][t+1],1ll*(mod-1)*f[now][j][k][t]%mod*p[i+1]%mod);
				add(f[nxt][j][k][t+1],1ll*f[now][j][k][t]*p[i+1]%mod);
				if(k+a[i+1]<=N/d)add(f[nxt][j][k+a[i+1]][t],1ll*(mod-1)*f[now][j][k][t]%mod*(mod+1-p[i+1])%mod);
				add(f[nxt][j][k][t],1ll*f[now][j][k][t]*(mod+1-p[i+1])%mod);
			}
		}int ans=0;
		F(j,0,N/d)F(k,0,N/d)F(t,0,M)if(f[M&1][j][k][t]){
			int n1=max(n-j*d,0),n2=N-(j+k)*d;
			if(n1>n2)continue;int v=0;
			if(!n1)v=C(n2+M-1,M-1);
			else for(int x=1;x<=t;x++)v+=1ll*C1[j][x-1]*C2[k][M-x]%mod,v-=v>=mod?mod:0;
			ans+=1ll*v*f[M&1][j][k][t]%mod,ans-=ans>=mod?mod:0;
		}pi(ans);
		return 0;
	}
}
signed main(){return EMT::main();}

最短路

暴力可以用高精度加上最短路进行求解,有30pts
考虑正解。一个数的最短距离在二进制表示上相比于前趋仅加了一位1,也就是找到从这一位往后连续的1区间置0并将下一位设成1.发现主席树可以很好地实现这个操作。于是用主席树计算哈希值实现比较大小和加操作即可。

Code



#include<vector>
#include<queue>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;typedef unsigned long long ull;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	#define getchar getchar_unlocked
	inline int read(){int x=0;char ch=getchar();while(ch<'0'||ch>'9')ch=getchar();while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x;}
	inline void file(){freopen("hellagur.in","r",stdin);freopen("hellagur.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=2e5+100,M=2e5+50,mod=1e9+7;struct dp{int x,v;};std::vector<dp>g[N];
	int n,m,Rt,bin[N],S,T,rt[N];ull p[N];
	inline int MM(int x){return x>=mod?x-mod:x;}
	struct seg{
		int ls[N*100],rs[N*100],siz[N*100],tot;ull hash[N*100];
		inline void up(int x,int len){
			siz[x]=siz[ls[x]]+siz[rs[x]];
			hash[x]=hash[ls[x]]+hash[rs[x]]*p[len];
		}
		inline int dfs(int x,int l,int r){
			if(l==r)return bin[l]*hash[x];
			int mid=(l+r)>>1;
			return MM(dfs(ls[x],l,mid)+dfs(rs[x],mid+1,r));
		}
		inline void build(int &p,int l,int r){
			p=++tot;
			if(l==r)return;
			int mid=(l+r)>>1;
			build(ls[p],l,mid),build(rs[p],mid+1,r);
			up(p,mid-l+1);
		}
		inline void insert(int x,int &y,int l,int r,int v){
			y=++tot,ls[y]=ls[x],rs[y]=rs[x],siz[y]=siz[x],hash[y]=hash[x];
			if(l==r){siz[y]=hash[y]=1;return;}
			int mid=(l+r)>>1;
			if(v<=mid)insert(ls[x],ls[y],l,mid,v);
			else insert(rs[x],rs[y],mid+1,r,v);
			up(y,mid-l+1);
		}
		inline int cov(int x,int rt0,int l,int r,int ql,int qr){
			if(l>=ql&&r<=qr)return rt0;
			int now=++tot;ls[now]=ls[x],rs[now]=rs[x],siz[now]=siz[x],hash[now]=hash[x];
			int mid=(l+r)>>1;
			if(qr<=mid)ls[now]=cov(ls[x],ls[rt0],l,mid,ql,qr);
			else if(ql>mid)rs[now]=cov(rs[x],rs[rt0],mid+1,r,ql,qr);
			else ls[now]=cov(ls[x],ls[rt0],l,mid,ql,mid),rs[now]=cov(rs[x],rs[rt0],mid+1,r,mid+1,qr);
			up(now,mid-l+1);return now;
		}
		inline ull gethash(int x,int l,int r,int ql,int qr){
			if(l>=ql&&r<=qr)return hash[x];
			int mid=(l+r)>>1;
			if(qr<=mid)return gethash(ls[x],l,mid,ql,qr);
			if(ql>mid)return gethash(rs[x],mid+1,r,ql,qr);
			return gethash(ls[x],l,mid,ql,mid)+gethash(rs[x],mid+1,r,mid+1,qr)*p[mid-ql+1];
		}
		inline int getsiz(int x,int l,int r,int ql,int qr){
			if(l>=ql&&r<=qr)return siz[x];
			int mid=(l+r)>>1;
			if(qr<=mid)return getsiz(ls[x],l,mid,ql,qr);
			if(ql>mid)return getsiz(rs[x],mid+1,r,ql,qr);
			return getsiz(ls[x],l,mid,ql,mid)+getsiz(rs[x],mid+1,r,mid+1,qr);
		}
		inline int find(int x,int l,int r,int v){
			if(l==r)return l;
			int mid=(l+r)>>1;
			if(v>mid)return find(rs[x],mid+1,r,v);
			if(getsiz(ls[x],l,mid,v,mid)==mid-v+1)
			return find(rs[x],mid+1,r,mid+1);
			else return find(ls[x],l,mid,v);
		}
		inline bool ask(int x,int y,int l,int r){
			if(l==r)return hash[x]<hash[y]?1:0;
			int mid=(l+r)>>1;
			if(hash[rs[x]]==hash[rs[y]])
			return ask(ls[x],ls[y],l,mid);
			else return ask(rs[x],rs[y],mid+1,r);
		}
	}segm;
	inline void ins(int x,int &y,int v){
		y=0;
		if(!segm.getsiz(x,0,M,v,v)){segm.insert(x,y,0,M,v);return;}
		int pos=segm.find(x,0,M,v);segm.insert(x,y,0,M,pos);
		if(pos==v)return;y=segm.cov(y,rt[0],0,M,v,pos-1);
	}
	inline bool judge(int x,int y){return segm.ask(x,y,0,M);}
	struct pd{int x,rt;friend bool operator <(pd a,pd b){return !segm.ask(a.rt,b.rt,1,n);}};
	inline void dij(){
		static bool vis[N],meet[N];
		std::priority_queue<pd>q;
		q.push({S,rt[S]});
		while(!q.empty()){
			int x=q.top().x;q.pop();
			if(vis[x])continue;
			for(auto w:g[x]){
				int y=w.x,v=w.v;
				if(vis[y])continue;
				if(!meet[y]){
					meet[y]=1;ins(rt[x],rt[y],v);q.push({y,rt[y]});
				}else{
					int Rt=0;ins(rt[x],Rt,v);
					if(judge(Rt,rt[y])){rt[y]=Rt;q.push({y,rt[y]});}
				}
			}
		}
	}
	inline bool judge(){
		static bool vis[N];static int q[N],hd,tl;
		q[hd=tl=1]=S;vis[S]=1;
		while(hd<=tl){
			int x=q[hd++];
			for(auto w:g[x])if(!vis[w.x])vis[w.x]=1,q[++tl]=w.x;
		}return vis[T];
	}
	inline short main(){
		file();
		n=read(),m=read();
		F(i,1,m){
			int x=read(),y=read(),z=read();
			g[x].push_back({y,z}),g[y].push_back({x,z});
		}
		bin[0]=p[0]=1;
		F(i,1,M)bin[i]=bin[i-1]<<1,bin[i]-=bin[i]>=mod?mod:0,p[i]=p[i-1]*131;
		S=read(),T=read();
		segm.build(rt[0],0,M);rt[S]=rt[0];if(!judge()){puts("-1");return 0;}
		dij();pi(segm.dfs(rt[T],0,M));
		return 0;
	}
}
signed main(){return EMT::main();}

集合

暴力打满有30pts,如果结论猜对就能拿到100pts高分
对于n为偶数,发现如果\(\frac{n}{2}\)为偶数可以直接去掉一个\(\frac{n}{2}\),否则直接去掉\(\frac{n}{2}\)\(2\)就能构造出一组解。
对于n为奇数,把n去掉变成偶数即可。
但是这只是合法解,不一定最优,于是可以用异或哈希来判断每个质数的出现奇偶性,依次判断是否有\(n,n-1,n-2\)即可。

Code


#include<cassert>
#include<unordered_map>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;typedef unsigned long long ull;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("mountain.in","r",stdin);freopen("mountain.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=1e6+10;
	int prime[N],co,mv[N],count[1<<20],bel[N],n,cnt[N],jc[22][22];bool vis[N];ull hash[N],val[N];
	inline void init(int n){
		F(i,2,n){
			if(!vis[i])prime[++co]=i,mv[i]=i,bel[i]=co,val[i]=1ll*rand()*rand();
			F(j,1,co){
				if(i*prime[j]>n)break;
				vis[prime[j]*i]=1;
				mv[prime[j]*i]=prime[j];
				if(i%prime[j]==0)break;
			}
		}
	}
	inline ull prepare(){
		ull hs=0;
		F(i,1,co){
			int v=prime[i];ll cnt=0;
			for(int j=v;j<=n;j+=v){
				int w=0,now=j;while(now%v==0)now/=v,w++;
				cnt+=1ll*w*(n-j+1);
			}if(cnt&1)hs^=val[prime[i]];
		}return hs;
	}
	std::unordered_map<ull,int>mp;
	inline void solve(){
		init(n);
		F(i,1,n){
			int now=i;hash[i]=hash[i-1];
			while(now!=1){
				int v=mv[now];
				hash[i]^=val[v];
				now/=v;
			}
		}
		ull hs=prepare();int key=0,key2=0;
		if(!hs){pi(n);pn();F(i,1,n)pi(i);return;}
		F(i,1,n)if(hash[i]==hs){key=i;break;}
		if(key){
			pi(n-1);pn();
			F(i,1,n)if(i!=key)pi(i);
			return;
		}
		F(i,1,n){
			if(!mp.count(hs^hash[i]))mp[hash[i]]=i;
			else{key=i;key2=mp[hs^hash[i]];break;}
		}
		if(key&&key2){
			pi(n-2);pn();
			F(i,1,n)if(i!=key&&i!=key2)pi(i);
		}else{
			assert(n&1);
			n--;
			pi(n-2);pn();
			F(i,1,n)if(i*2!=n&&i!=2)pi(i);
		}
	}
	inline short main(){
        file();
        srand((unsigned long long)(new int));
		n=read();
		if(n<=20){
			init(n);
			F(i,1,n){
				memcpy(jc[i],jc[i-1],sizeof(jc[i-1]));
				int now=i;
				while(now!=1){
					int v=mv[now];
					jc[i][bel[v]]++;
					now/=v;
				}
			}int ans1=0,ans2=0;
			for(int i=0;i<(1<<n);i++){
				count[i]=count[i>>1]+(i&1);
				if(ans1>=count[i])continue;
				memset(cnt,0,sizeof(int)*(co+1));
				F(j,1,n)if(i&(1<<(j-1)))
					F(l,1,co)cnt[l]+=jc[j][l];
				bool fl=1;
				F(l,1,co)if(cnt[l]&1){fl=0;break;}
				if(fl)ans1=count[i],ans2=i;
			}
			pi(ans1);pn();
			F(i,1,n)if(ans2&(1<<(i-1)))pi(i);
		}else solve();
		return 0;
	}
}
signed main(){return EMT::main();}

考场上没想到如何转移区间dp,然后就寄了
分三个转移,分别表示从\(l\)\(r\)全部删干净的最大价值,从\(l\)\(r\)恰好删剩下一个最大值,从\(l\)\(r\)恰好删剩下一个最小值的最大价值。
然后最后用\(n^2\)dp合并一下就行了。

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("good.in","r",stdin);freopen("good.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=410;
	int f[N][N],v[N],a[N],n,g1[N][N],g2[N][N],dp[N];
	inline short main(){
		file();
		n=read();
		F(i,1,n)v[i]=read();
		F(i,1,n)a[i]=read();
		memset(f,-0x3f,sizeof(f));
		memset(g1,-0x3f,sizeof(f));
		memset(g2,-0x3f,sizeof(f));
		F(i,1,n)f[i][i]=v[1],f[i][i-1]=g1[i][i]=g2[i][i]=0;
		F(len,2,n)F(l,1,n-len+1){
			int r=l+len-1;
			F(p,l,r-1)if(a[p]==a[r]-1)g1[l][r]=max(g1[l][r],g1[l][p]+f[p+1][r-1]);
			F(p,l+1,r)if(a[p]==a[l]-1)g2[l][r]=max(g2[l][r],f[l+1][p-1]+g2[p][r]);
			F(p,l,r-1)f[l][r]=max(f[l][r],f[l][p]+f[p+1][r]);
			F(p,l,r)if(a[p]>=a[l]&&a[p]>=a[r]&&a[p]*2-a[l]-a[r]+1<=n)f[l][r]=max(f[l][r],g1[l][p]+g2[p][r]+v[a[p]*2-a[l]-a[r]+1]);
		}
		F(i,1,n){
			dp[i]=dp[i-1];
			D(j,i-1,0)dp[i]=max(dp[i],dp[j]+f[j+1][i]);
		}pi(dp[n]);
		return 0;
	}
}
signed main(){return EMT::main();}

将询问分块可以做到\(nlog_n\)
但是这题数据太水了导致直接暴力就能过。

Code


#include<vector>
#include<cstring>
#include<iostream>
#include<queue>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<unordered_map>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	#define pb push_back
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("color.in","r",stdin);freopen("color.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=2e5+10;
	struct dp{int v,id;friend bool operator <(dp a,dp b){return a.v>b.v;}};
	struct edge{int u,v,w;}e[N<<1];std::vector<dp>g[N];std::priority_queue<dp>q,del;
	std::unordered_map<int,int>mp[N];
	int col[N];
	inline int getans(){
		while(1){
			if(!q.size())return 0;
			if(del.size()&&q.top().id==del.top().id)q.pop(),del.pop();
			else return q.top().v;
		}
	}
	int n,m,c,Q;
	inline short main(){
		file();
		n=read(),m=read(),c=read(),Q=read();
		F(i,1,m)e[i].u=read(),e[i].v=read(),e[i].w=read();
		F(i,1,n)col[i]=read();
		F(i,1,m){
			int x=e[i].u,y=e[i].v;g[x].pb({y,i}),g[y].pb({x,i});
			if(col[x]!=col[y])q.push({e[i].w,i});
		}
		while(Q--){
			int x=read(),y=read();
			if(y==col[x]){pi(getans());pn();continue;}
			for(auto w:g[x])
				if(col[w.v]==col[x])q.push({e[w.id].w,w.id});
				else if(col[w.v]==y)del.push({e[w.id].w,w.id});
			col[x]=y;
			pi(getans());pn();
		}
		return 0;
	}
}
signed main(){return EMT::main();}

\(f_i\)表示长度为\(i\)的串合法的方案数,考虑容斥,那么有:

\[f_i=\prod\limits_{j=1}^iv_j-\sum\limits_{j=1}^{\frac{i}{2}}f_j\prod\limits_{k=j+1}^{i-j}v_k \]

求出前缀积后发现很像分治NTT,然后考虑用其优化一下。
具体就是递归到\(l,r\)时用\(l,mid\)\(f\)卷上\(mid+1,r\)\(s\),然后到单点时将\(f_l\)的贡献统计到\(f_{l*2}\)里面。

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("music.in","r",stdin);freopen("music.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=4e6+10,mod=998244353;
	int n,v[N],pre[N],inv[N],f[N],g[N];
	using std::copy;
	inline int M(int x){return x>=mod?x-mod:x;}
	inline int ksm(int a,int b){
		int ans=1;
		while(b){
			if(b&1)ans=1ll*a*ans%mod;
			a=1ll*a*a%mod;b>>=1;
		}return ans;
	}
	namespace NTT{
		int r[N],G[N],Gi[N];
		inline void init(int lim,int l){
			for(int i=0;i<lim;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
			G[0]=1,G[1]=ksm(3,(mod-1)/lim);
			for(int i=2;i<lim;i++)G[i]=1ll*G[i-1]*G[1]%mod;
			Gi[0]=1,Gi[1]=ksm(G[1],mod-2);
			for(int i=2;i<lim;i++)Gi[i]=1ll*Gi[i-1]*Gi[1]%mod;
		}
		inline void ntt(int *a,int tp,int lim){
			for(int i=0;i<lim;i++)if(i<r[i])std::swap(a[i],a[r[i]]);
			for(int mid=1;mid<lim;mid<<=1){
				for(int j=0;j<lim;j+=(mid<<1)){
					for(int k=0;k<mid;k++){
						int w=tp==1?G[lim/(mid<<1)*k]:Gi[lim/(mid<<1)*k];
						int x=a[j+k],y=1ll*a[j+mid+k]*w%mod;
						a[j+k]=M(x+y);
						a[j+mid+k]=M(x-y+mod);
					}
				}
			}
			if(tp==-1){
				int inv=ksm(lim,mod-2);
				for(int i=0;i<lim;i++)a[i]=1ll*a[i]*inv%mod;
			}
		}
		inline void mul(int *a,int *b,int n,int m,int *c){
			static int A[N],B[N];int lim=1,l=0;
			while(lim<=n+m)lim<<=1,l++;init(lim,l);
			copy(a,a+n,A),copy(b,b+m,B);
			ntt(A,1,lim),ntt(B,1,lim);
			for(int i=0;i<lim;i++)A[i]=1ll*A[i]*B[i]%mod;
			ntt(A,-1,lim);copy(A,A+n+m-1,c);
			memset(A,0,lim<<2),memset(B,0,lim<<2);
		}
		inline void solve(int l,int r){
			static int A[N],B[N],C[N];
			if(l==r){f[l]=M(-g[l]+pre[l]+mod);g[l<<1]=M(g[l<<1]+f[l]);return;}
			int mid=(l+r)>>1;
			solve(l,mid);
			for(int i=l;i<=mid;i++)A[i-l]=1ll*f[i]*inv[i]%mod;//,pi(A[i-l]);pn();
			for(int i=mid+1;i<=r;i++)B[i-mid-1]=pre[i];//,pi(B[i-mid-1]);pn();
			mul(A,B,mid-l+1,r-mid,C);
			for(int i=0;i<=r-l+1;i++)g[i+l+mid+1]=M(g[i+l+mid+1]+C[i]);//,pi(C[i]);pn();
			memset(A,0,(mid-l+1)<<2),memset(B,0,(r-mid)<<2),memset(C,0,(r-l+1)<<2);
			solve(mid+1,r);
		}
	}
	inline void initinv(){
		static int pr[N],sf[N];
		pr[0]=sf[n+1]=1;
		F(i,1,n)pr[i]=1ll*pr[i-1]*pre[i]%mod;
		D(i,n,1)sf[i]=1ll*sf[i+1]*pre[i]%mod;
		int Inv=ksm(pr[n],mod-2);
		F(i,1,n)inv[i]=1ll*Inv*pr[i-1]%mod*sf[i+1]%mod;
	}
	inline short main(){
		file();
		pre[0]=1;n=read();
		F(i,1,n)v[i]=read(),pre[i]=1ll*pre[i-1]*v[i]%mod;
		initinv();
		NTT::solve(1,n);
		pi(f[n]);
		return 0;
	}
}
signed main(){return EMT::main();}

回文子串

发现K的范围很小于是复杂度可以扩展到\(O(KNlogN)\)
分偶数和奇数,在两颗线段树上分别维护每个回文中心最大回文长度支持区间赋值,区间求和单点修改即可。
具体的话,举个奇数例子,当修改[l,r]的串时,[l+k,r-k]的半径一定是k,
然后对于[l-k+1,l+k-1],[r-k+1,r+k-1]的半径可以用哈希线段树暴力判断修改。
查询就是[l+k,r-k]区间求和,[l,l+k-1],[r-k+1,r]一个一个和k取min求和

Code


#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<random>
namespace EMT{
    typedef long long ll;typedef double db;typedef unsigned long long ull;
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    #define pf printf
    std::mt19937 rnd((unsigned long long)(new char));
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=51000,base=131;
    char s[N];ull pw[N],pre[N];int n,K,len1,len2;bool vis[N];
    //哈希线段树
    struct seg1{
        ull sum[N<<2];int lz[N<<2];
        inline void up(int p,int l,int r){
            int mid=(l+r)>>1;
            sum[p]=sum[p<<1]*pw[r-mid]+sum[p<<1|1];
        }
        inline void build(int p,int l,int r){
            if(l==r){sum[p]=s[l];return;}
            int mid=(l+r)>>1;
            build(p<<1,l,mid),build(p<<1|1,mid+1,r);
            up(p,l,r);
        }
        inline void down(int p,int l,int r){
            if(lz[p]){
                int mid=(l+r)>>1;
                sum[p<<1]=pre[mid-l]*lz[p];
                sum[p<<1|1]=pre[r-mid-1]*lz[p];
                lz[p<<1]=lz[p<<1|1]=lz[p];
                lz[p]=0;
            }
        }
        inline void change(int p,int l,int r,int ql,int qr,int v){
            if(l>=ql&&r<=qr){
                sum[p]=pre[r-l]*v;
                // pi(l);pi(r);pi(ql);pi(qr);pf("%llu\n",sum[p]);
                lz[p]=v;return;
            }int mid=(l+r)>>1;down(p,l,r);
            if(qr<=mid)change(p<<1,l,mid,ql,qr,v);
            else if(ql>mid)change(p<<1|1,mid+1,r,ql,qr,v);
            else change(p<<1,l,mid,ql,mid,v),change(p<<1|1,mid+1,r,mid+1,qr,v);
            up(p,l,r);
            // pi(l);pi(r);pi(ql);pi(qr);pf("%llu\n",sum[p]);
        }
        inline ull ask(int p,int l,int r,int ql,int qr){
            if(l>=ql&&r<=qr)return sum[p];
            int mid=(l+r)>>1;down(p,l,r);
            if(qr<=mid)return ask(p<<1,l,mid,ql,qr);
            if(ql>mid)return ask(p<<1|1,mid+1,r,ql,qr);
            return ask(p<<1,l,mid,ql,mid)*pw[qr-mid]+ask(p<<1|1,mid+1,r,mid+1,qr);
        }
    }segm1;
    //哈希线段树
    struct seg2{
        ull sum[N<<2];int lz[N<<2];
        inline void up(int p,int l,int r){
            int mid=(l+r)>>1;
            sum[p]=sum[p<<1]+sum[p<<1|1]*pw[mid-l+1];
        }
        inline void build(int p,int l,int r){
            if(l==r){sum[p]=s[l];return;}
            int mid=(l+r)>>1;
            build(p<<1,l,mid),build(p<<1|1,mid+1,r);
            up(p,l,r);
        }
        inline void down(int p,int l,int r){
            if(lz[p]){
                int mid=(l+r)>>1;
                sum[p<<1]=pre[mid-l]*lz[p];
                sum[p<<1|1]=pre[r-mid-1]*lz[p];
                lz[p<<1]=lz[p<<1|1]=lz[p];
                lz[p]=0;
            }
        }
        inline void change(int p,int l,int r,int ql,int qr,int v){
            if(l>=ql&&r<=qr){
                sum[p]=pre[r-l]*v;
                // pi(l);pi(r);pi(ql);pi(qr);pf("%llu\n",sum[p]);
                lz[p]=v;return;
            }int mid=(l+r)>>1;down(p,l,r);
            if(qr<=mid)change(p<<1,l,mid,ql,qr,v);
            else if(ql>mid)change(p<<1|1,mid+1,r,ql,qr,v);
            else change(p<<1,l,mid,ql,mid,v),change(p<<1|1,mid+1,r,mid+1,qr,v);
            up(p,l,r);
            // pi(l);pi(r);pi(ql);pi(qr);pf("%llu\n",sum[p]);
        }
        inline ull ask(int p,int l,int r,int ql,int qr){
            if(l>=ql&&r<=qr)return sum[p];
            int mid=(l+r)>>1;down(p,l,r);
            if(qr<=mid)return ask(p<<1,l,mid,ql,qr);
            if(ql>mid)return ask(p<<1|1,mid+1,r,ql,qr);
            return ask(p<<1,l,mid,ql,mid)+ask(p<<1|1,mid+1,r,mid+1,qr)*pw[mid-ql+1];
        }
    }segm2;
    //回文半径线段树
    struct seg3{
        int sum[N<<2],lz[N<<2];
        inline void down(int p,int l,int r){
            if(lz[p]){
                int mid=(l+r)>>1;
                sum[p<<1]=lz[p]*(mid-l+1);
                sum[p<<1|1]=lz[p]*(r-mid);
                lz[p<<1]=lz[p<<1|1]=lz[p];
                lz[p]=0;
            }
        }
        inline void change(int p,int l,int r,int ql,int qr,int v){
            if(l>=ql&&r<=qr){
                sum[p]=v*(r-l+1);
                lz[p]=v;return;
            }int mid=(l+r)>>1;down(p,l,r);
            if(qr<=mid)change(p<<1,l,mid,ql,qr,v);
            else if(ql>mid)change(p<<1|1,mid+1,r,ql,qr,v);
            else change(p<<1,l,mid,ql,mid,v),change(p<<1|1,mid+1,r,mid+1,qr,v);
            sum[p]=sum[p<<1]+sum[p<<1|1];
        }
        inline int ask(int p,int l,int r,int ql,int qr){
            if(l>=ql&&r<=qr)return sum[p];
            int mid=(l+r)>>1;down(p,l,r);
            if(qr<=mid)return ask(p<<1,l,mid,ql,qr);
            if(ql>mid)return ask(p<<1|1,mid+1,r,ql,qr);
            return ask(p<<1,l,mid,ql,mid)+ask(p<<1|1,mid+1,r,mid+1,qr);
        }
    }segm3,segm4;
    inline void getlen1(int i){
        int l=2,r=min(i,min(len1,n-i+1)),ans=1;
        // pi(r);pn();
        while(l<=r){
            int mid=(l+r)>>1;
            if(segm1.ask(1,1,n,i-mid+1,i)==segm2.ask(1,1,n,i,i+mid-1))l=mid+1,ans=mid;
            else r=mid-1;
        }segm3.change(1,1,n,i,i,ans);
        // pf("len1 %d %d\n",i,ans);
    }
    inline void getlen2(int i){
        int l=1,r=min(i,min(len2,n-i)),ans=0;
        // pi(r);pn();
        while(l<=r){
            int mid=(l+r)>>1;
            if(segm1.ask(1,1,n,i-mid+1,i)==segm2.ask(1,1,n,i+1,i+mid))l=mid+1,ans=mid;
            else r=mid-1;
        }segm4.change(1,1,n,i,i,ans);
        // pf("len2 %d %d\n",i,ans);
    }
    inline int gc(){char ch=getchar();while(ch<'a'||ch>'z')ch=getchar();return ch;}
    inline short main(){
        //file();
        scanf("%s",s+1);n=strlen(s+1);pw[0]=1;
        F(i,1,n)pw[i]=pw[i-1]*base;pre[0]=1;
        F(i,1,n)pre[i]=pre[i-1]+pw[i];
        segm1.build(1,1,n),segm2.build(1,1,n);
        K=read();len1=(K+1)/2,len2=K/2;
        F(i,1,n)getlen1(i);	
        F(i,1,n-1)getlen2(i);
        int Q=read();
        while(Q--){
            int opt=read(),l=read(),r=read();
            if(opt==1){
                int ch=gc();
                segm1.change(1,1,n,l,r,ch);segm2.change(1,1,n,l,r,ch);
                // pf("%llu %llu\n",segm1.ask(1,1,n,1,1),segm2.ask(1,1,n,3,3));
                F(i,max(1,l+1-len1),min(n,l+len1-1))getlen1(i);
                F(i,max(1,r-len1+1),min(n,r+len1-1))getlen1(i);
                F(i,max(1,l-len2),min(n-1,l+len2-1))getlen2(i);
                F(i,max(1,r-len2),min(n-1,r+len2-1))getlen2(i);
                if(l+len1<=r-len1)segm3.change(1,1,n,l+len1,r-len1,len1);
                if(l+len2<=r-len2-1)segm4.change(1,1,n,l+len2,r-len2-1,len2);
            }else{
                int ans=0;
                if(l+len1<=r-len1)ans+=segm3.ask(1,1,n,l+len1,r-len1);
                if(l+len2<=r-len2-1)ans+=segm4.ask(1,1,n,l+len2,r-len2-1);
                F(i,l,min(l+len1-1,r))vis[i]=1,ans+=min(min(i-l+1,r-i+1),segm3.ask(1,1,n,i,i));
                F(i,max(l,r-len1+1),r)if(!vis[i])vis[i]=1,ans+=min(min(i-l+1,r-i+1),segm3.ask(1,1,n,i,i));
                F(i,l,min(l+len1-1,r))vis[i]=0;F(i,max(l,r-len1+1),r)vis[i]=0;
                F(i,l,min(r-1,l+len2-1))vis[i]=1,ans+=min(min(i-l+1,r-i),segm4.ask(1,1,n,i,i));
                F(i,max(l,r-len2),r-1)if(!vis[i])vis[i]=1,ans+=min(min(i-l+1,r-i),segm4.ask(1,1,n,i,i));
                F(i,l,min(r-1,l+len2-1))vis[i]=0;F(i,max(l,r-len2),r-1)vis[i]=0;
                pi(ans);pn();
            }
        }
        return 0;
    }
}
signed main(){return EMT::main();}

recollection

题目中给出了一颗trie树,然后我们可以用这颗trie树建出广义SAM,然后在parent tree上进行子树合并,在set中插入节点的dfn序,显然一个点匹配的最大的是dfn最靠近的,于是在父节点上合并时更新一下答案即可。然后由于字符集大小比较大,可以使用map等容器建边。

Code
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<unordered_map>
#include<set>
#include<vector>
namespace EMT{
    typedef long long ll;typedef double db;
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    #define pf printf
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=4e5+10;
    struct tire{std::vector<int>son[N];int col[N],fa[N];}T;
    struct node{int next,to;}e[N];int n,pos[N],ans,head[N],co;
    inline void add(int next,int to){e[++co]={head[next],to},head[next]=co;}
    //fail树上树剖
    namespace sp{
        int top[N],siz[N],son[N],fa[N],dfn[N],ti,time[N],deep[N];
        inline void dfs1(int k){
            siz[k]=1;
            for(int i=head[k];i;i=e[i].next){
                int j=e[i].to;deep[j]=deep[k]+1;fa[j]=k;
                dfs1(j);siz[k]+=siz[j];if(siz[son[k]]<siz[j])son[k]=j;
            }
        }
        inline void dfs2(int k,int tp){
            top[k]=tp;time[dfn[k]=++ti]=k;
            if(!son[k])return;
            dfs2(son[k],tp);
            for(int i=head[k];i;i=e[i].next)if(e[i].to!=son[k])dfs2(e[i].to,e[i].to);
        }
        inline int getlca(int x,int y){
            while(top[x]!=top[y])if(deep[top[x]]>=deep[top[y]])x=fa[top[x]];else y=fa[top[y]];
            return deep[x]>deep[y]?y:x;
        }
    }
    namespace SAM{
        struct trie{
            std::unordered_map<int,int>son;
            int len,fa;
        }t[N];int tot=1;
        #define son(x,v) t[x].son[v]
        #define len(x) t[x].len
        #define fa(x) t[x].fa
        inline int insert(int v,int last){
            int p=last,np=++tot;len(np)=len(p)+1;
            for(;p&&!son(p,v);p=fa(p))son(p,v)=np;
            if(!p)fa(np)=1;
            else{
                int q=son(p,v);
                if(len(q)==len(p)+1)fa(np)=q;
                else{
                    int nq=++tot;t[nq]=t[q];
                    len(nq)=len(p)+1,fa(np)=fa(q)=nq;
                    for(;p&&son(p,v)==q;p=fa(p))son(p,v)=nq;
                }
            }return np;
        }
        inline void build(){
            static int q[N],hd=1,tl;
            pos[1]=1;
            for(auto j:T.son[1])q[++tl]=j;
            while(hd<=tl){
                int x=q[hd++];
                pos[x]=insert(T.col[x],pos[T.fa[x]]);
                for(auto i:T.son[x])q[++tl]=i;
            }
            F(i,2,tot)add(fa(i),i);sp::dfs1(1);sp::dfs2(1,1);
        }
        std::set<int>s[N];
        inline void dfs(int k,int deep){
            s[k].insert(sp::dfn[pos[k]]);
            for(auto i:T.son[k]){
                dfs(i,deep+1);
                if(s[k].size()<s[i].size())s[k].swap(s[i]);
                for(auto x:s[i]){
                    auto it=s[k].insert(x).first,it2=it;
                    if(it!=s[k].begin()){
                        it--;
                        int lca=sp::getlca(sp::time[x],sp::time[*it]);
                        ans=max(ans,len(lca)+deep);
                    }
                    it2++;if(it2!=s[k].end()){
                        int lca=sp::getlca(sp::time[x],sp::time[*it2]);
                        ans=max(ans,len(lca)+deep);
                    }
                }
            }
        }
        inline void print(){
            F(i,1,n)pi(pos[i]);pn();
            F(x,1,tot){
                pi(x);pi(len(x));pn();
                for(int i=head[x];i;i=e[i].next)pi(e[i].to);pn();
            }
        }
        inline void solve(){dfs(1,0);}
        #undef son
        #undef len
        #undef fa
    }
    inline short main(){
        //file();
        n=read();
        F(i,2,n){int x=read();T.col[i]=read();T.fa[i]=x;T.son[x].push_back(i);}
        SAM::build();SAM::solve();//SAM::print();
        pi(ans);
        return 0;
    }
}
signed main(){return EMT::main();}

回忆树

我们先建出后缀自动机,在parent tree上分成u到lca,lca到v以及中间一段询问串长度处理。
中间那段直接用hash暴力求解,
剩下两端,u到lca那一段让询问串在SAM上反着跑一遍,lca到v那段正着跑一遍,然后parent tree树上主席树,查询有多少子树内节点即可。

Code


#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;typedef unsigned long long ull;
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    #define pf printf
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=2e5+10;
    int rt[N],bel[N],n,m,pos[N],dfn[N],last[N],ti,siz[N<<1],deep[N],val[N];
    char s[N];
    struct trie{
        int son[N][26],tot=1,col[N],fa[N];
        inline int insert(int now,int v){
            if(!son[now][v])son[now][v]=++tot,col[tot]=v,fa[tot]=now;
            return son[now][v];
        }
    }T;
    struct seg{
        int sum[N*60],ls[N*60],rs[N*60],tot;
        inline void insert(int x,int &y,int l,int r,int vv){
            y=++tot;ls[y]=ls[x],rs[y]=rs[x],sum[y]=sum[x]+1;
            if(l==r)return;int mid=(l+r)>>1;
            if(vv<=mid)insert(ls[x],ls[y],l,mid,vv);
            else insert(rs[x],rs[y],mid+1,r,vv);
        }
        inline int ask(int x,int y,int l,int r,int ql,int qr){
            if(sum[x]==sum[y])return 0;
            if(l>=ql&&r<=qr)return sum[y]-sum[x];
            int mid=(l+r)>>1;
            if(qr<=mid)return ask(ls[x],ls[y],l,mid,ql,qr);
            if(ql>mid)return ask(rs[x],rs[y],mid+1,r,ql,qr);
            return ask(ls[x],ls[y],l,mid,ql,mid)+ask(rs[x],rs[y],mid+1,r,mid+1,qr);
        }
    }segm;
    namespace parenttree{
        int co,head[N<<1];
        struct node{int next,to;}e[N<<1];
        inline void add(int next,int to){e[++co]={head[next],to},head[next]=co;}
        inline void dfs(int k){
            dfn[k]=++ti;siz[k]=1;//pf("parent: ");pi(k);pn();
            //for(int i=head[k];i;i=e[i].next)pi(e[i].to);pn();
            for(int i=head[k];i;i=e[i].next)dfs(e[i].to),siz[k]+=siz[e[i].to];
            last[k]=ti;
        }
    }
    namespace tree{
        int fa[N][22],log[N],head[N],co;
        struct node{int next,to,w;}e[N<<1];
        inline void add(int next,int to,int w){e[++co]={head[next],to,w},head[next]=co;}
        inline void dfs1(int k){
            for(int i=head[k],j;i;i=e[i].next)if((j=e[i].to)!=fa[k][0]){
                fa[j][0]=k;deep[j]=deep[k]+1,log[deep[j]]=log[deep[j]>>1]+1;
                F(l,1,log[deep[j]])fa[j][l]=fa[fa[j][l-1]][l-1];val[j]=e[i].w;
                bel[j]=T.insert(bel[k],e[i].w-'a');
                dfs1(j);
            }
        }
        inline void dfs2(int k){
            for(int i=head[k],j;i;i=e[i].next)if((j=e[i].to)!=fa[k][0])
                segm.insert(rt[k],rt[j],1,ti,dfn[pos[bel[j]]]),dfs2(j);
        }
        inline int getfa(int x,int k){D(i,log[deep[x]],0)if(k>=(1<<i))x=fa[x][i],k-=1<<i;return x;}
        inline int getlca(int a,int b){
            if(deep[a]<deep[b])a^=b^=a^=b;
            D(i,log[deep[a]],0)if((1<<i)<=deep[a]-deep[b])a=fa[a][i];
            if(a==b)return a;
            D(i,log[deep[a]],0)if(fa[a][i]!=fa[b][i])a=fa[a][i],b=fa[b][i];
            return fa[a][0];
        }
        char ss[N];ull hash[N],p[N];
        inline void init(){p[0]=1;F(i,1,n)p[i]=p[i-1]*131;}
        inline int force(int x,int y,int lca,int len2){
            ull hs=0;F(i,1,len2)hs=hs*131+s[i];
            int len1=deep[x]+deep[y]-deep[lca]*2,l=0;
            while(x!=lca)ss[++l]=val[x],x=fa[x][0];l=len1;while(y!=lca)ss[l--]=val[y],y=fa[y][0];
            F(i,1,len1)hash[i]=hash[i-1]*131+ss[i];int ans=0;
            F(i,len2,len1)if(hash[i]-hash[i-len2]*p[len2]==hs)ans++;
            return ans;
        }
        inline void print(){
            F(i,1,n)pi(bel[i]);pn();
            F(i,1,T.tot)pi(pos[i]);pn();
            F(i,1,ti)pi(siz[i]);pn();
            F(i,1,ti)pi(dfn[i]);pn();
            F(i,1,ti)pi(last[i]);pn();
        }
    }
    namespace SAM{
        int tot=1;
        struct trie{
            int son[26],len,fa;
        }t[N<<1];
        #define son(x,v) t[x].son[v]
        #define len(x) t[x].len
        #define fa(x) t[x].fa
        inline int insert(int v,int last){
            int p=last,np=++tot;len(np)=len(p)+1;
            for(;p&&!son(p,v);p=fa(p))son(p,v)=np;
            if(!p)fa(np)=1;
            else{
                int q=son(p,v);
                if(len(q)==len(p)+1)fa(np)=q;
                else{
                    int nq=++tot;t[nq]=t[q];
                    len(nq)=len(p)+1,fa(np)=fa(q)=nq;
                    for(;p&&son(p,v)==q;p=fa(p))son(p,v)=nq;
                }
            }return np;
        }
        inline void build(){
            static int q[N],hd=1,tl;
            F(i,0,25)if(T.son[1][i])q[++tl]=T.son[1][i];
            pos[1]=1;
            while(hd<=tl){
                int x=q[hd++];
                pos[x]=insert(T.col[x],pos[T.fa[x]]);
                F(i,0,25)if(T.son[x][i])q[++tl]=T.son[x][i];
            }F(i,2,tot)parenttree::add(fa(i),i);parenttree::dfs(1);
        }
        inline void solve(int x,int y,int n){
            //pf("Case:\n");
            int lca=tree::getlca(x,y),ans=0,xfa,yfa;
            xfa=deep[x]-deep[lca]>n?tree::getfa(x,deep[x]-deep[lca]-n):x,yfa=deep[y]-deep[lca]>n?tree::getfa(y,deep[y]-deep[lca]-n):y;
            //pi(xfa),pi(yfa),pn();
            ans=tree::force(xfa,yfa,lca,n);//pi(ans);pn();
            if(deep[x]-deep[lca]>n){
                int now=1;
                D(i,n,1){int v=s[i]-'a';now=son(now,v);if(!now)break;}//pf("一 ");pi(now);pn();
                if(now)ans+=segm.ask(rt[xfa],rt[x],1,ti,dfn[now],last[now]);
            }
            if(deep[y]-deep[lca]>n){
                int now=1;
                F(i,1,n){int v=s[i]-'a';now=son(now,v);if(!now)break;}//pf("二 ");pi(now);pn();
                if(now)ans+=segm.ask(rt[yfa],rt[y],1,ti,dfn[now],last[now]);
            }pi(ans);pn();
        }
        #undef son
        #undef len
        #undef fa
    }
    inline short main(){
        //file();
        n=read(),m=read();
        F(i,2,n){
            int x=read(),y=read();char ch=getchar();
            while(ch<'a'||ch>'z')ch=getchar();
            tree::add(x,y,ch),tree::add(y,x,ch);
        }bel[1]=1;tree::log[0]=-1;tree::dfs1(1);SAM::build();tree::dfs2(1);tree::init();//tree::print();
        while(m--){int x=read(),y=read();scanf("%s",s+1);int len=strlen(s+1);SAM::solve(x,y,len);}
        return 0;
    }
}
signed main(){return EMT::main();}

虚空恶魔

建出后缀自动机之后,记录下每个节点endpos的最大值和最小值,然后在parent树上跑一遍拓扑,让自己的最大值和最小值组合一下,分相交和不相交讨论一下更新答案即可。

Code


#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    #define pf printf
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline ll max(ll a,ll b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
    const int N=4e6+10;
    char s[N];int n;
    namespace SAM{
        struct trie{int son[26],len,fa,p1,p2;}t[N<<1];
        int last=1,tot=1;
        #define son(x,v) t[x].son[v]
        #define len(x) t[x].len
        #define fa(x) t[x].fa
        #define p1(x) t[x].p1
        #define p2(x) t[x].p2
        inline void insert(int v,int id){
            int p=last,np=last=++tot;len(np)=len(p)+1,p1(np)=p2(np)=id;
            for(;p&&!son(p,v);p=fa(p))son(p,v)=np;
            if(!p)fa(np)=1;
            else{
                int q=son(p,v);
                if(len(q)==len(p)+1)fa(np)=q;
                else{
                    int nq=++tot;t[nq]=t[q];
                    len(nq)=len(p)+1,fa(q)=fa(np)=nq;
                    for(;p&&son(p,v)==q;p=fa(p))son(p,v)=nq;
                }
            }
        }
        inline void topo(){
            static int b[N],id[N];
            F(i,1,tot)b[len(i)]++;
            F(i,1,n)b[i]+=b[i-1];
            F(i,2,tot)id[b[len(i)]--]=i;
            D(i,tot,2){
                int x=id[i];
                p1(fa(x))=max(p1(fa(x)),p1(x)),p2(fa(x))=min(p2(fa(x)),p2(x));
            }ll ans=0;
            F(i,1,tot){
                int x=min(p1(i)-p2(i),len(i));
                if(x==len(i))ans=max(ans,1ll*x*max(p1(i)-x,n-p2(i)));
                else ans=max(ans,1ll*x*max(p2(i),n-p2(i)));
            }
            pi(ans);
        }
    }
    inline short main(){
        //file();
        scanf("%s",s+1);n=strlen(s+1);
        F(i,1,n)SAM::insert(s[i]-'a',i);
        SAM::topo();
        return 0;
    }
}
signed main(){return EMT::main();}

高维入侵

发现题目让求不同border能组成多少种长度不同的序列,一开始读成本质不同了然后就去世了。。。
求出border之后,有个性质:
对于任意一个字符串,它的border长度一定会构成log个等差数列。
然后我们对于每个数进行同余最短路,用单调队列求出到达每个点最小代价\(f_i\)
之后对每个等差数列合并,我们把新的f看成g,则:

\[g_i=\min\limits_{f_j \% now=i}f_j \]

具体就是走的步数不变,只有模数变了,那么就可以这样转移了。
然后对于原来的x0单独转移一下即可。

Code


#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;typedef unsigned long long ull;
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    #define pf printf
    inline ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline ll max(ll a,ll b){return a>b?a:b;}inline ll min(ll a,ll b){return a<b?a:b;}
    inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
    const int N=2e6+10;ull hash[N],p[N];int bod[N],n,now;ll f[N],W;char s[N];
    inline ull gethash(int l,int r){return hash[r]-hash[l-1]*p[r-l+1];}
    inline void cmod(int x){
        int gcd=std::__gcd(x,now),top=0;
        static int s[N<<1];static ll pre[N];
        memcpy(pre,f,sizeof(ll)*now);memset(f,0x3f,sizeof(ll)*x);
        F(i,0,now-1){int tmp=pre[i]%x;f[tmp]=min(f[tmp],pre[i]);}
        int nnow=now%x;
        F(i,0,gcd-1){
            top=0;s[++top]=i;int tmp=i+nnow;tmp-=tmp>=x?x:0;
            while(tmp!=s[1])s[++top]=tmp,tmp+=nnow,tmp-=tmp>=x?x:0;
            F(j,1,top)s[j+top]=s[j];top<<=1;
            F(j,2,top)f[s[j]]=min(f[s[j]],f[s[j-1]]+now);
        }now=x;
    }
    inline void solve(int x0,int d,int len){
        int gcd=std::__gcd(x0,d),hd,tl,top;
        static int que[N],s[N],q[N];static ll w[N];cmod(x0);if(d<0)return;
        int nd=d%now;
        F(i,0,gcd-1){
            top=0,que[0]=0;que[++que[0]]=i;
            int tmp=i+nd;tmp-=tmp>=now?now:0;while(tmp!=que[1])que[++que[0]]=tmp,tmp+=nd,tmp-=tmp>=now?now:0;
            int st=1;F(j,2,que[0])if(f[que[j]]<f[que[st]])st=j;
            F(j,st,que[0])s[++top]=que[j];F(j,1,st-1)s[++top]=que[j];
            q[hd=tl=1]=1;w[1]=f[s[1]]-d;
            F(j,2,top){
                while(hd<=tl&&q[hd]+len<j)hd++;
                if(hd<=tl)f[s[j]]=min(f[s[j]],w[hd]+1ll*j*d+x0);
                while(hd<=tl&&w[tl]>=f[s[j]]-1ll*j*d)tl--;
                w[++tl]=f[s[j]]-1ll*j*d,q[tl]=j;
            }
        }
    }
    inline short main(){
        //file();
        int T=1;
        while(T--){
            n=read(),W=read()-n;scanf("%s",s+1);p[0]=1;memset(bod,0,sizeof(int)*(bod[0]+1));
            F(i,1,n)p[i]=p[i-1]*131,hash[i]=hash[i-1]*131+s[i];
            F(i,1,n-1)if(hash[i]==gethash(n-i+1,n))bod[++bod[0]]=i;
            F(i,1,bod[0])bod[i]=n-bod[i];std::reverse(bod+1,bod+bod[0]+1);
            now=n;memset(f,0x3f,sizeof(ll)*now);f[0]=0;
            F(i,1,bod[0]){
                int j=i;while(bod[i+1]-bod[i]==bod[j+1]-bod[j])j++;
                solve(bod[i],bod[i+1]-bod[i],j-i-1);i=j-1;
            }ll ans=0;
            F(i,0,now-1)if(W>=f[i])ans+=(W-f[i])/now+1;
            pi(ans);pn();
        }
        return 0;
    }
}
signed main(){return EMT::main();}

Surprise me

对1-n每个数建出虚树之后容斥即可。首先答案是:

\[\frac{1}{n(n-1)}\sum\limits_{i=1}^n\sum\limits_{j=1}^n\phi(a_ia_j)*dis(i,j) \]

由于\(\phi_x=x\prod\limits_{x\%prime_i==0} \frac{prime_i-1}{prime_i}\)
扔掉前面的系数,于是可以转化成:

\[\sum\limits_{i=1}^n\sum\limits_{j-1}^n\frac{\phi{a_i}\times\phi_{a_j}\times gcd(a_i,a_j)}{\phi_{gcd(a_i,a_j)}}*(deep_i+deep_j-2deep_{lca}) \]

考虑枚举gcd于是变成:

\[\sum\limits_{d=1}^n\frac{d}{\phi_d}\sum\limits_{i=1}^n\sum\limits_{j=1}^n[gcd(a_i,a_j)==d]\phi_{a_i}\times\phi_{a_j}\times dis(i,j) \]

设后面这一坨是\(f(d)\)再设:

\[F(x)=\sum\limits_{x|d}f(d) \]

由莫比乌斯反演得到:

\[f(x)=\sum\limits_{x|d}\mu(\frac{d}{x})F_d \]

大F很好求

\[F_d=\sum\limits_{i=1}^n\sum\limits_{j=1}^n[d|gcd(a_i,a_j)]\phi_{a_i}\times\phi_{a_j}\times dis(i,j) \]

然后把\(a_i \% d==0\)的拿下来建虚树即可。

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;//(double)clock() / (double)CLOCKS_PER_SEC;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=2e5+10,mod=1e9+7;
    int G[N],g[N],n,deep[N],mu[N],a[N],prime[N],inv[N],phi[N],bel[N];bool vis[N];
    struct pre{
        int dfn[N],top[N],siz[N],son[N],fa[N],head[N],co,ti;
        struct node{int next,to;}e[N<<1];
        inline void add(int next,int to){e[++co]={head[next],to},head[next]=co;}
        inline void dfs1(int k){
            siz[k]=1;
            for(int i=head[k],j;i;i=e[i].next)if((j=e[i].to)!=fa[k]){
                deep[j]=deep[k]+1,fa[j]=k,dfs1(j);siz[k]+=siz[j];
                if(siz[son[k]]<siz[j])son[k]=j;
            }
        }
        inline void dfs2(int k,int tp){
            top[k]=tp;dfn[k]=++ti;
            if(!son[k])return;
            dfs2(son[k],tp);
            for(int i=head[k],j;i;i=e[i].next)if((j=e[i].to)!=fa[k]&&j!=son[k])dfs2(j,j);
        }
        inline int getlca(int x,int y){
            while(top[x]!=top[y])if(deep[top[x]]>=deep[top[y]])x=fa[top[x]];else y=fa[top[y]];
            return deep[x]<deep[y]?x:y;
        }
    }o;
    struct unreal{
        int rec[N],f[N],s[N],top,head[N],co,ans;bool vis[N];
        struct node{int next,to;}e[N];
        inline void add(int next,int to){e[++co]={head[next],to},head[next]=co;}
        inline void ins(int x){
            if(!top){s[top=1]=x;return;}
            int lca=o.getlca(s[top],x);
            while(top>1&&deep[lca]<deep[s[top-1]]){
                add(s[top-1],s[top]);
                top--;
            }if(deep[lca]<deep[s[top]])add(lca,s[top--]);
            if((!top)||(s[top]!=lca))s[++top]=lca;
            s[++top]=x;
        }
        inline void build(int v){
            rec[0]=co=ans=top=0;
            F(i,1,n/v)rec[++rec[0]]=bel[i*v],vis[bel[i*v]]=1;
            if(!vis[1])rec[++rec[0]]=1;
            std::sort(rec+1,rec+rec[0]+1,[](int i,int j){return o.dfn[i]<o.dfn[j];});
            s[top=1]=rec[1];
            F(i,2,rec[0])ins(rec[i]);
            while(--top)add(s[top],s[top+1]);
        }
        inline void dfs(int k,int fa){
            if(vis[k])f[k]=phi[a[k]];else f[k]=0;
            ans-=2ll*f[k]*f[k]*deep[k]%mod,ans+=ans<0?mod:0;
            for(int i=head[k],j;i;i=e[i].next)if((j=e[i].to)!=fa){
                dfs(j,k);ans-=2ll*f[j]*f[k]%mod*deep[k]%mod*2%mod,ans+=ans<0?mod:0;
                f[k]+=f[j];f[k]-=f[k]>=mod?mod:0;
            }head[k]=0;vis[k]=0;
        }
        inline void getans(){
            int sum2=0,sum1=0;
            F(i,1,rec[0])if(vis[rec[i]])sum1+=1ll*phi[a[rec[i]]]*deep[rec[i]]%mod,sum1-=sum1>=mod?mod:0,sum2+=phi[a[rec[i]]],sum2-=sum2>=mod?mod:0;
            ans=2ll*sum1*sum2%mod;
            dfs(1,0);
        }
    }tree;
    inline void init(){
        mu[1]=phi[1]=1;
        F(i,2,n)if(!phi[i])F(j,1,n/i){
            if(!phi[j*i])phi[j*i]=i*j;
            phi[i*j]=phi[i*j]/i*(i-1);
        }
        F(i,2,n){
            if(!vis[i]){prime[++prime[0]]=i;mu[i]=-1;}
            F(j,1,prime[0]){
                if(i*prime[j]>n)break;
                vis[prime[j]*i]=1;
                if(i%prime[j]==0)break;
                mu[i*prime[j]]=-mu[i];
            }
        }
    }
    inline int getG(int x){
        if(G[x]!=-1)return G[x];G[x]=0;
        tree.build(x);tree.getans();
        return G[x]=tree.ans;
    }
    inline int getg(int x){
        if(g[x]!=-1)return g[x];g[x]=0;
        F(i,1,n/x)g[x]+=1ll*mu[i]*G[i*x],g[x]-=g[x]>=mod?mod:0,g[x]+=g[x]<0?mod:0;
        return g[x];
    }
    inline int ksm(int a,int b){
        int ans=1;
        while(b){
            if(b&1)ans=1ll*a*ans%mod;
            a=1ll*a*a%mod;b>>=1;
        }return ans;
    }
    inline short main(){
        //file();
        memset(g,-1,sizeof(g));memset(G,-1,sizeof(G));
        n=read();init();
        F(i,1,n)a[i]=read(),bel[a[i]]=i;
        F(i,2,n){
            int x=read(),y=read();
            o.add(x,y),o.add(y,x);
        }o.dfs1(1);o.dfs2(1,1);
        inv[0]=inv[1]=1;
        F(i,2,n)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
        int ans=0;
        F(i,1,n)getG(i);
        F(i,1,n)ans+=1ll*getg(i)*inv[phi[i]]%mod*i%mod,ans-=ans>=mod?mod:0;
        pi(1ll*ans*ksm(1ll*n*(n-1)%mod,mod-2)%mod);
        return 0;
    }
}
signed main(){return EMT::main();}

神牛养成计划

卡空间没脸题,由于仅需要是前后缀,于是建出两颗trie树(用链式前向星)后,在一颗树上建立另一颗树的主席树就可以直接回答询问了。

Code


#include<map>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
namespace EMT{
    typedef long long ll;typedef double db;//(double)clock() / (double)CLOCKS_PER_SEC;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=2100,M=2000010;
    char s[M];
    int n,m,lans,end1[N],end2[N],rt[M];
    inline int uncode(int x){return (x-'a'+lans)%26+'a';}
    struct seg{
        int siz[N*30],ls[N*30],rs[N*30],tot;
        inline void insert(int x,int &y,int l,int r,int v){
            if(!y)y=++tot,ls[y]=ls[x],rs[y]=rs[x],siz[y]=siz[x];siz[y]++;
            if(l==r)return;int mid=(l+r)>>1;
            if(v<=mid){
                if(ls[x]==ls[y])ls[y]=0;
                insert(ls[x],ls[y],l,mid,v);
            }else{
                if(rs[x]==rs[y])rs[y]=0;
                insert(rs[x],rs[y],mid+1,r,v);
            }
        }
        inline int ask(int x,int y,int l,int r,int ql,int qr){
            if(siz[x]==siz[y])return 0;
            if(l>=ql&&r<=qr)return siz[y]-siz[x];
            int mid=(l+r)>>1;
            if(qr<=mid)return ask(ls[x],ls[y],l,mid,ql,qr);
            if(ql>mid)return ask(rs[x],rs[y],mid+1,r,ql,qr);
            return ask(ls[x],ls[y],l,mid,ql,mid)+ask(rs[x],rs[y],mid+1,r,mid+1,qr);
        }
    }segm;
    std::vector<int>hv[M];
    struct trie{
        int dfn[M],last[M],ti,time[M],tot=1,head[M],co;
        struct node{int next,to,w;}e[M];
        inline void add(int next,int to,int w){e[++co]={head[next],to,w},head[next]=co;}
        inline int insert(int n){
            int now=1;
            F(i,1,n){
                int v=s[i]-'a',x=0;
                for(int j=head[now];j;j=e[j].next)if(e[j].w==v){x=e[j].to;break;}
                if(!x)x=++tot,add(now,x,v);
                now=x;
            }return now;
        }
        inline int run(int n){
            int now=1;
            F(i,1,n){
                int v=s[i]-'a',x=0;
                for(int j=head[now];j;j=e[j].next)if(e[j].w==v){x=e[j].to;break;}
                if(!x)return 0;
                now=x;
            }return now;
        }
        inline void dfs(int k){
            time[dfn[k]=++ti]=k;
            for(int i=head[k];i;i=e[i].next)dfs(e[i].to);
            last[k]=ti;
        }
    }t1,t2;
    inline short main(){
        //file();
        n=read();
        F(i,1,n){
            scanf("%s",s+1);int len=strlen(s+1);
            end1[i]=t1.insert(len);
            std::reverse(s+1,s+len+1);
            end2[i]=t2.insert(len);
        }t1.dfs(1);t2.dfs(1);
        F(i,1,n)hv[end1[i]].push_back(t2.dfn[end2[i]]);
        F(i,1,t1.ti){
            for(auto x:hv[t1.time[i]])segm.insert(rt[i-1],rt[i],1,t2.ti,x);
            if(!rt[i])rt[i]=rt[i-1];
        }
        m=read();
        while(m--){
            scanf("%s",s+1);int len=strlen(s+1);
            F(i,1,len)s[i]=uncode(s[i]);
            int x=t1.run(len);
            scanf("%s",s+1);
            if(!x){pi(lans=0);pn();continue;}
            len=strlen(s+1);
            std::reverse(s+1,s+len+1);
            F(i,1,len)s[i]=uncode(s[i]);
            int y=t2.run(len);if(!y){pi(lans=0);pn();continue;}
            pi(lans=segm.ask(rt[t1.dfn[x]-1],rt[t1.last[x]],1,t2.ti,t2.dfn[y],t2.last[y]));pn();
        }
        return 0;
    }
}
signed main(){return EMT::main();}

二分一个答案,每次check时,在这个位置的值出现的上个位置last之后区间加,对每个位置在线段树内暴力删除所有大于mid的点,最多删k次就return,即可求解,然而考场上写丑了和暴力一个分。。。
复杂度是\(O(nlog_wlog_n)\)的。

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<unordered_map>
namespace EMT{
    typedef long long ll;typedef double db;//(double)clock() / (double)CLOCKS_PER_SEC;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline ll max(ll a,ll b){return a>b?a:b;}inline ll min(ll a,ll b){return a<b?a:b;}
    inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
    const int N=1e5+10;const ll inf=1e14+10;
    int n,K,k,a[N],s[N],last[N];ll now;std::unordered_map<int,int>mp;
    struct dp{ll val,mn;friend dp operator +(dp a,dp b){return {max(a.val,b.val),min(a.mn,b.mn)};}};
    struct seg{
        dp t[N<<2];ll lz[N<<2];
        inline void build(int p,int l,int r){
            lz[p]=t[p].val=t[p].mn=0;
            if(l==r)return;
            int mid=(l+r)>>1;
            build(p<<1,l,mid),build(p<<1|1,mid+1,r);
            t[p]=t[p<<1]+t[p<<1|1];
        }
        inline void down(int p){
            lz[p<<1]+=lz[p],lz[p<<1|1]+=lz[p];
            t[p<<1].val+=lz[p],t[p<<1|1].val+=lz[p];
            lz[p]=0;
        }
        inline void change(int p,int l,int r,int ql,int qr,int v){
            if(l>=ql&&r<=qr){
                t[p].val+=v;
                t[p].mn+=v;
                lz[p]+=v;
                return;
            }int mid=(l+r)>>1;down(p);
            if(qr<=mid)change(p<<1,l,mid,ql,qr,v);
            else if(ql>mid)change(p<<1|1,mid+1,r,ql,qr,v);
            else change(p<<1,l,mid,ql,mid,v),change(p<<1|1,mid+1,r,mid+1,qr,v);
            t[p]=t[p<<1]+t[p<<1|1];
        }
        inline void ask(int p,int l,int r,int ql,int qr){
            if(t[p].mn>=now){K-=r-l+1;return;}
            if(K<=0||t[p].val<=now)return;
            if(l==r){K--;return;}
            if(l>=ql&&r<=qr){
                int mid=(l+r)>>1;if(lz[p])down(p);
                if(t[p<<1].val>now)ask(p<<1,l,mid,ql,qr);
                if(t[p<<1|1].val>now)ask(p<<1|1,mid+1,r,ql,qr);
            }else{
                int mid=(l+r)>>1;if(lz[p])down(p);
                if(qr<=mid)ask(p<<1,l,mid,ql,qr);
                else if(ql>mid)ask(p<<1|1,mid+1,r,ql,qr);
                else ask(p<<1,l,mid,ql,mid),ask(p<<1|1,mid+1,r,mid+1,qr);
            }
        }
    }segm;
    inline bool check(ll x){
        now=x;K=k;segm.build(1,1,n);
        F(i,1,n){
            segm.change(1,1,n,last[i]+1,i,a[i]);
            segm.ask(1,1,n,1,i);if(K<=0)return 0;
        }
        return 1;
    }
    inline short main(){
    //	file();
        n=read();k=read();
        F(i,1,n)a[i]=read();
        F(i,1,n)last[i]=mp[a[i]],mp[a[i]]=i;
        ll l=-inf,r=inf,ans=0;
        while(l<=r){
            ll mid=(l+r)>>1;
            if(check(mid))r=mid-1,ans=mid;
            else l=mid+1;
        }pi(ans);
        return 0;
    }
}
signed main(){return EMT::main();}

Q0Y1NzFE

发现前两个操作就是并查集连边,于是把询问离线下来,剩下的就是区间修改单点查询。具体就是先查询x在第二颗树上所在的版本,然后在第一颗树的主席树上查询一下答案即可。

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;//(double)clock() / (double)CLOCKS_PER_SEC;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
    const int N=5e5+10;
    struct qus{int opt,x,y;}q[N];
    int n,m,rt[N];
    struct seg1{
        int ls[N*23],rs[N*23],tot;ll lz[N*23];
        inline void change(int x,int &y,int l,int r,int ql,int qr,int v){
            y=++tot,ls[y]=ls[x],rs[y]=rs[x],lz[y]=lz[x];
            if(l>=ql&&r<=qr){
                lz[y]+=v;
                return;
            }int mid=(l+r)>>1;
            if(qr<=mid)change(ls[x],ls[y],l,mid,ql,qr,v);
            else if(ql>mid)change(rs[x],rs[y],mid+1,r,ql,qr,v);
            else change(ls[x],ls[y],l,mid,ql,mid,v),change(rs[x],rs[y],mid+1,r,mid+1,qr,v);
        }
        inline ll ask(int x,int y,int l,int r,int v){
            if(l==r)return lz[y]-lz[x];
            int mid=(l+r)>>1;
            if(v<=mid)return lz[y]-lz[x]+ask(ls[x],ls[y],l,mid,v);
            else return lz[y]-lz[x]+ask(rs[x],rs[y],mid+1,r,v);
        }
    }segm1;
    struct seg2{
        int lz[N<<2];
        inline void down(int p){
            lz[p<<1]=lz[p<<1|1]=lz[p];
            lz[p]=0;
        }
        inline void change(int p,int l,int r,int ql,int qr,int v){
            if(l>=ql&&r<=qr){
                lz[p]=v;
                return;
            }int mid=(l+r)>>1;if(lz[p])down(p);
            if(qr<=mid)change(p<<1,l,mid,ql,qr,v);
            else if(ql>mid)change(p<<1|1,mid+1,r,ql,qr,v);
            else change(p<<1,l,mid,ql,mid,v),change(p<<1|1,mid+1,r,mid+1,qr,v);
        }
        inline int ask(int p,int l,int r,int x){
            if(l==r)return lz[p];
            int mid=(l+r)>>1;if(lz[p])down(p);
            if(x<=mid)return ask(p<<1,l,mid,x);else return ask(p<<1|1,mid+1,r,x);
        }
    }segm2;
    struct lg{
        int head[N],co,dfn[N],last[N],ti,fa[N],l[N],r[N];
        struct node{int next,to,last;}e[N];
        inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
        inline void add(int next,int to){e[++co]={head[next],to},head[next]=co;}
        inline void rev(){
            F(x,1,n){
                int lst=0;
                for(int i=head[x];i;i=e[i].next)
                    e[i].last=lst,lst=head[x]=i;
            }
        }
        inline void dfs(int k){
            dfn[k]=++ti;
            for(int i=head[k];i;i=e[i].last)dfs(e[i].to);
            last[k]=ti;
        }
        inline void init(){
            F(i,1,n)fa[i]=i;
        }
        inline void getdfn(){
            F(i,1,n)if(!dfn[i])dfs(find(i));
        }
        inline void inlr(){
            F(i,1,n)l[i]=r[i]=dfn[i];
        }
    }o1,o2;
    inline short main(){
        //file();
        n=read(),m=read();
        o1.init(),o2.init();
        F(i,1,m){
            char ch=getchar();
            while(ch!='U'&&ch!='M'&&ch!='A'&&ch!='Z'&&ch!='Q')ch=getchar();
            if(ch=='U')q[i].opt=1,q[i].x=read(),q[i].y=read();
            if(ch=='M')q[i].opt=2,q[i].x=read(),q[i].y=read();
            if(ch=='A')q[i].opt=3,q[i].x=read();
            if(ch=='Z')q[i].opt=4,q[i].x=read();
            if(ch=='Q')q[i].opt=5,q[i].x=read();
        }
        F(i,1,m){
            if(q[i].opt==1){
                int fx=o1.find(q[i].x),fy=o1.find(q[i].y);
                o1.add(fx,fy);o1.fa[fy]=fx;
            }
            if(q[i].opt==2){
                int fx=o2.find(q[i].x),fy=o2.find(q[i].y);
                o2.add(fx,fy);o2.fa[fy]=fx;
            }
        }o1.rev();o2.rev();
        o1.getdfn(),o2.getdfn();
        o1.init(),o2.init();
        o1.inlr(),o2.inlr();
        F(i,1,m){
            rt[i]=rt[i-1];
            if(q[i].opt==1){
                int fx=o1.find(q[i].x),fy=o1.find(q[i].y);
                o1.fa[fy]=fx,o1.r[fx]=o1.r[fy];
            }
            if(q[i].opt==2){
                int fx=o2.find(q[i].x),fy=o2.find(q[i].y);
                o2.fa[fy]=fx,o2.r[fx]=o2.r[fy];
            }
            if(q[i].opt==3){
                int fx=o1.find(q[i].x);
                segm1.change(rt[i-1],rt[i],1,n,o1.l[fx],o1.r[fx],o1.r[fx]-o1.l[fx]+1);
            }
            if(q[i].opt==4){
                int fx=o2.find(q[i].x);
                segm2.change(1,1,n,o2.l[fx],o2.r[fx],i);
            }
            if(q[i].opt==5){
                int lst=segm2.ask(1,1,n,o2.dfn[q[i].x]);
                pi(segm1.ask(rt[lst],rt[i],1,n,o1.dfn[q[i].x]));pn();
            }
        }
        return 0;
    }
}
signed main(){return EMT::main();}

U1BPSiBDT1Q2

合并子树凸包,用set维护一下即可。
具体的话就是启发式合并,插入一个点的时候左边弹一弹,右边弹一弹,看一下自己能不能在凸包内,能就加入,每个点的答案是和\(2pre_fa\)斜率最接近的那条直线在凸包上的值。

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<set>
namespace EMT{
    typedef long long ll;typedef double db;typedef std::pair<ll,ll> pll;
    #define mp(x,y) std::make_pair(x,y)
    #define fir first
    #define sec second
    #define pf printf
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}inline ll max(ll a,ll b){return a>b?a:b;}inline ll min(ll a,ll b){return a<b?a:b;}
    inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}inline ll sqr(ll x){return x*x;}
    inline db slope(pll x,pll y){return 1.0*(y.sec-x.sec)/(y.fir-x.fir);}
    inline bool cmp(pll a,pll b,pll c){return slope(a,b)<slope(b,c);}
    const int N=1e6+10;const ll inf=1e14;std::set<pll>s[N];
    ll pre[N],tag[N],f[N],fa[N];int head[N],co,n;struct node{int next,to;}e[N];
    inline void add(int next,int to){e[++co]={head[next],to},head[next]=co;}
    inline ll get(int x){
        if(s[x].empty())return inf;pll ans;
        if(s[x].size()==1)ans=*s[x].begin();
        else{
            ll l=s[x].begin()->fir,r=(--s[x].end())->fir,an=0;
            while(l<=r){
                ll mid=(l+r)>>1;
                auto it=s[x].lower_bound(mp(mid,-inf));
                pll a=*it,b=*(--it);
                a.sec+=tag[x],b.sec+=tag[x];
                if(slope(a,b)<2*pre[fa[x]])l=mid+1,an=mid;
                else r=mid-1;
            }ans=*s[x].lower_bound(mp(an,-inf));
        }ans.sec+=tag[x];
        return ans.sec+sqr(pre[fa[x]])-2*pre[fa[x]]*ans.fir;
    }
    inline void insert(std::set<pll>&s,pll x){
        auto it=s.lower_bound(x);
        if(it!=s.end()&&it->fir==x.fir){
            if(it->sec<=x.sec)return;
            else s.erase(it);
        }
        while(!s.empty()){
            auto it=s.lower_bound(x);
            if(it==s.begin())break;
            --it;
            if(it==s.begin())break;
            auto it2=it;it2--;
            if(!cmp(*it2,*it,x))s.erase(it);else break;
        }
        while(!s.empty()){
            auto it=s.lower_bound(x);
            if(it==s.end())break;
            auto it2=it;it2++;
            if(it2==s.end())break;
            if(!cmp(x,*it,*it2))s.erase(it);else break;
        }
        it=s.lower_bound(x);
        if(it==s.begin()||it==s.end()){s.insert(x);return;}
        auto it2=it;it2--;
        if(cmp(*it2,x,*it))s.insert(x);
    }
    inline void dfs(int k){
        ll sum=0,son=0;
        pre[k]+=pre[fa[k]];
        for(int i=head[k],j;i;i=e[i].next){
            j=e[i].to;dfs(j);sum+=f[j];
            if(s[j].size()>s[son].size())son=j;
        }s[k].swap(s[son]);tag[k]=tag[son]+sum-f[son];
        for(int i=head[k],j;i;i=e[i].next)if((j=e[i].to)!=son){
            tag[j]+=sum-f[j];
            for(auto x:s[j])x.sec+=tag[j],x.sec-=tag[k],insert(s[k],x);
        }
        f[k]=min(get(k),sum+sqr(pre[k]-pre[fa[k]]));
        insert(s[k],mp(pre[k],sqr(pre[k])+sum-tag[k]));
    }
    inline short main(){
        //file();
        n=read();F(i,1,n)pre[i]=read();
        F(i,2,n)fa[i]=read(),add(fa[i],i);
        dfs(1);pi(f[1]);
        return 0;
    }
}
signed main(){return EMT::main();}

序列

思博题。首先值很好求,在一颗普通线段树内,发现答案是

\[\max\{\max\{mxtag_i,val_i\}+adtag_i,v\} \]

\[\max\{\max\{mxtag_i,val_i\},v-adtag_i\}+adtag_i \]

两个变量就是直接意思,于是维护一下里面的值最大值即可。
然后次数,加操作一定有贡献,设\(pre_i\)为到i位置一共加了多少值,于是对于一个max操作,j能在i后进行当且仅当

\[mx_i+pre_j-pre_i<mx_j \]

于是维护\(mx_i-pre_i\)支持区间求以\(a_i\)为首最长上升子序列就是操作次数,
具体就是比较左子树最大值,如果比\(a_i\)大就递归到左子树加上右子树长度否则去询问右子树,
update就是把左子树最大值放到右子树询问,因为一定是单调递增的。

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline ll max(ll a,ll b){return a>b?a:b;}inline ll min(ll a,ll b){return a<b?a:b;}
    inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
    const int N=1e5+10;const ll inf=1e15;bool is[N];
    struct dp{int opt,id,v;};std::vector<dp>hv[N];
    struct pd{ll val;int len;};int n,m,a[N],ans2[N];ll ans1[N];
    struct seg{
        pd t[N<<2];ll lz[N<<2];int lv[N<<2];
        inline void down(int p){
            if(lz[p]){
                t[p<<1].val+=lz[p],t[p<<1|1].val+=lz[p],
                lz[p<<1]+=lz[p],lz[p<<1|1]+=lz[p];
                lz[p]=0;
            }
        }
        inline pd ask(int p,int l,int r,int ql,int qr,ll v){
            // pi(p);pi(l);pi(r);pi(ql);pi(qr);pi(v);pi(lv[p]);pi(t[p].val);pi(t[p].len);pn();
            if(!lv[p])return {-inf,0};
            if(l>=ql&&r<=qr){
                int mid=(l+r)>>1;
                if(l==r)return t[p].val>v?(pd){t[p].val,1}:(pd){-inf,0};
                down(p);
                if(lv[p<<1]&&t[p<<1].val>v){
                    pd x=ask(p<<1,l,mid,ql,qr,v);
                    return {max(x.val,lv[p<<1|1]?t[p<<1|1].val:-inf),x.len+t[p].len-t[p<<1].len};
                }else return ask(p<<1|1,mid+1,r,ql,qr,v);
            }else{
                int mid=(l+r)>>1;down(p);
                if(qr<=mid)return ask(p<<1,l,mid,ql,qr,v);
                if(ql>mid)return ask(p<<1|1,mid+1,r,ql,qr,v);
                pd x=ask(p<<1,l,mid,ql,mid,v),y=ask(p<<1|1,mid+1,r,mid+1,qr,x.val==-inf?v:x.val);
                return {max(x.val,y.val),x.len+y.len};
            }
        }
        inline void up(int p,int l,int r){
            if(lv[p<<1]||lv[p<<1|1])lv[p]=1;else lv[p]=0,t[p].len=0;
            // pf("up:\n");pi(p);pi(l);pi(r);pi(lv[p]);pn();
            if(!lv[p])return;int mid=(l+r)>>1;
            if(lv[p<<1]&&lv[p<<1|1]){
                t[p].val=max(t[p<<1].val,t[p<<1|1].val);
                t[p].len=t[p<<1].len+ask(p<<1|1,mid+1,r,mid+1,r,t[p<<1].val).len;
            }else if(lv[p<<1])t[p]=t[p<<1];else t[p]=t[p<<1|1];
            // pf("up and live: %d %d %d %d\n",l,r,t[p].val,t[p].len);
        }
        inline void add(int p,int l,int r,int ql,int qr,int v){
            // pi(p);pi(l);pi(r);pi(ql);pi(qr);pi(v);pn();
            if(l>=ql&&r<=qr){
                lz[p]+=v,t[p].val+=v;
                return;
            }int mid=(l+r)>>1;down(p);
            if(qr<=mid)add(p<<1,l,mid,ql,qr,v);
            else if(ql>mid)add(p<<1|1,mid+1,r,ql,qr,v);
            else add(p<<1,l,mid,ql,mid,v),add(p<<1|1,mid+1,r,mid+1,qr,v);
            up(p,l,r);
            // pi(l);pi(r);pi(t[p].val);pi(t[p].len);pn();
        }
        inline void insert(int p,int l,int r,int x,int v){
            if(l==r){t[p].val+=v;t[p].len=1;lv[p]=1;return;}
            int mid=(l+r)>>1;down(p);
            if(x<=mid)insert(p<<1,l,mid,x,v);else insert(p<<1|1,mid+1,r,x,v);
            up(p,l,r);
            // pi(l);pi(r);pi(t[p].val);pi(t[p].len);pn();
        }
        inline void del(int p,int l,int r,int x){
            // pi(p);pi(l);pi(r);pi(x);pi(t[p].val);pi(t[p].len);pn();
            if(l==r){t[p].val=-inf,t[p].len=0,lv[p]=0;return;}
            int mid=(l+r)>>1;down(p);
            if(x<=mid)del(p<<1,l,mid,x);else del(p<<1|1,mid+1,r,x);
            up(p,l,r);
            // pi(l);pi(r);pi(t[p].val);pi(t[p].len);pn();
        }
    }segm;
    struct Seg2{
        ll lz1[N<<2],lz2[N<<2];int val[N<<2];
        inline void build(int p,int l,int r){
            lz1[p]=-inf;
            if(l==r){val[p]=a[l];return;}
            int mid=(l+r)>>1;
            build(p<<1,l,mid),build(p<<1|1,mid+1,r);
        }
        inline void cg(int p,int v){lz1[p]=max(lz1[p],v-lz2[p]);}
        inline void down(int p){
            if(lz1[p]!=-inf)cg(p<<1,lz1[p]),cg(p<<1|1,lz1[p]),lz1[p]=-inf;
            if(lz2[p])lz2[p<<1]+=lz2[p],lz2[p<<1|1]+=lz2[p],lz2[p]=0;
        }
        inline void change(int p,int l,int r,int ql,int qr,int v){
            if(l>=ql&&r<=qr){cg(p,v);return;}
            int mid=(l+r)>>1;down(p);
            if(qr<=mid)change(p<<1,l,mid,ql,qr,v);
            else if(ql>mid)change(p<<1|1,mid+1,r,ql,qr,v);
            else change(p<<1,l,mid,ql,mid,v),change(p<<1|1,mid+1,r,mid+1,qr,v);
        }
        inline void add(int p,int l,int r,int ql,int qr,int v){
            if(l>=ql&&r<=qr){lz2[p]+=v;return;}
            int mid=(l+r)>>1;down(p);
            if(qr<=mid)add(p<<1,l,mid,ql,qr,v);
            else if(ql>mid)add(p<<1|1,mid+1,r,ql,qr,v);
            else add(p<<1,l,mid,ql,mid,v),add(p<<1|1,mid+1,r,mid+1,qr,v);
        }
        inline ll ask(int p,int l,int r,int x){
            if(l==r)return max(lz1[p],val[p])+lz2[p];
            int mid=(l+r)>>1;down(p);
            if(x<=mid)return ask(p<<1,l,mid,x);
            return ask(p<<1|1,mid+1,r,x);
        }
    }segm2;
    struct BIT{
        int t[N];
        inline void add(int x,int v){while(x<=m)t[x]+=v,x+=x&-x;}
        inline int ask(int x){int ans=0;while(x)ans+=t[x],x-=x&-x;return ans;}
        inline void add(int l,int r,int v){add(l,v),add(r+1,-v);}
    }bit;
    inline int abs(int x){return x<0?-x:x;}
    inline short main(){
    //	file();
        n=read();
        F(i,1,n)a[i]=read();
        segm2.build(1,1,n);
        m=read();
        F(i,1,m){
            char ch=getchar();
            while(ch!='Q'&&ch!='A'&&ch!='M')ch=getchar();
            if(ch=='Q'){
                int x=read();
                hv[x].push_back({3,i,0});
                is[i]=1;
                ans1[i]=segm2.ask(1,1,n,x);
            }
            if(ch=='A'){
                int l=read(),r=read(),v=read();
                if(v==0)continue;
                hv[l].push_back({1,i,v});
                hv[r+1].push_back({1,-i,-v});
                segm2.add(1,1,n,l,r,v);
            }
            if(ch=='M'){
                int l=read(),r=read(),v=read();
                hv[l].push_back({2,i,v});
                hv[r+1].push_back({2,-i,v});
                segm2.change(1,1,n,l,r,v);
            }
        }
        F(i,1,n){
            for(auto x:hv[i]){
                // pf("Case:\n");
                // pi(i);pi(x.opt);pi(x.id);pi(x.v);pn();pn();
                if(x.opt==1){
                    if(!x.v)continue;
                    if(x.id>0)bit.add(x.id,m,1);
                    else bit.add(-x.id,m,-1);
                    segm.add(1,1,m,abs(x.id),m,-x.v);
                }
                if(x.opt==2){
                    if(x.id>0)segm.insert(1,1,m,x.id,x.v);
                    else segm.del(1,1,m,-x.id);
                }
                if(x.opt==3){
                    ans2[x.id]=bit.ask(x.id)+segm.ask(1,1,m,1,x.id,a[i]).len;
                }
            }
        }
        F(i,1,m)if(is[i])pi(ans1[i]),pi(ans2[i]),pn();
        return 0;
    }
}
signed main(){return EMT::main();}

旅行计划

首先发现K为奇数时答案一定是0,因为可以通过来回走k次实现\(\% k=0\)
然后就是K是偶数,先把联通块内所有边取gcd然后dfs看两点之间能不能形成偶数长度,如果能就直接是0,因为可以通过反复走偶数来实现\(\% k=0\)。否则就一定是1乘上和k取的gcd。

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=1e5+10;
    inline int gcd(int a,int b){return b?gcd(b,a%b):a;}
    int gd[N],fa[N],n,m,q,head[N],co,dis[N][3];
    inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    struct node{int next,to,w;}e[N<<1];
    inline void add(int next,int to,int w){e[++co]={head[next],to,w},head[next]=co;}
    inline void dfs(int k,int fl){
        if(dis[k][fl])return;dis[k][fl]=1;
        for(int i=head[k];i;i=e[i].next)dfs(e[i].to,fl^(e[i].w&1));
    }
    inline short main(){
        n=read(),m=read(),q=read();
        F(i,1,n)fa[i]=i;
        F(i,1,m){
            int x=read(),y=read(),z=read(),fx=find(x),fy=find(y);
            add(x,y,z),add(y,x,z);if(fx!=fy)fa[fx]=fy;
        }
        F(x,1,n)for(int i=head[x];i;i=e[i].next){
            int fx=find(x);
            if(!gd[fx])gd[fx]=e[i].w;
            gd[fx]=gcd(gd[fx],e[i].w);
        }
        F(x,1,n)for(int i=head[x];i;i=e[i].next)e[i].w/=gd[find(x)];
        F(i,1,n)if(fa[i]==i)dfs(i,0);
        while(q--){
            int x=read(),y=read(),k=read();
            if(find(x)!=find(y)){puts("NIE");continue;}
            int g=gcd(gd[find(x)],k),fl=(dis[x][0]&dis[y][0])|(dis[x][1]&dis[y][1]);
            if(fl){pi(0);pn();continue;}
            pi(g);pn();
        }
        return 0;
    }
}
signed main(){return EMT::main();}

Hack

一看是最小割,考虑网络流。但是边可以反复走。
因为可以反复走,所以两点之间的反向边连inf,只加入有关的点,
然后正边连边权,求最小割即可。

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    #define int long long
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%lld ",x);}inline void pn(){pf("\n");}
    const int N=1e4+10,inf=0x3f3f3f3f3f3f3f3f;
    int head[N],co=1,Hd[N],s,t,n,m;bool vis1[N],vis2[N];
    struct node{int next,to,w,fl;}e[N];
    inline void add(int next,int to,int w){e[++co]={head[next],to,w},head[next]=co;}
    namespace pc{
        inline void bfs(int st,bool vis[N],int Fl){
            static int q[N],hd,tl;
            q[hd=tl=1]=st;
            while(hd<=tl){
                int x=q[hd++];vis[x]=1;
                for(int i=head[x],j;i;i=e[i].next)if(e[i].fl==Fl)
                    if(!vis[j=e[i].to])q[++tl]=j;
            }
        }
    }
    namespace dinic{
        int dis[N];
        inline bool bfs(){
            static int q[N],hd,tl;
            memset(dis,0x3f,sizeof(int)*(n+1));dis[s]=0;
            memcpy(head,Hd,sizeof(int)*(n+1));q[hd=tl=1]=s;
            while(hd<=tl){
                int x=q[hd++];
                for(int i=head[x],j;i;i=e[i].next)if(e[i].w&&e[i].fl<3){
                    if(dis[j=e[i].to]>dis[x]+1&&vis1[j]&&vis2[j]){
                        dis[j]=dis[x]+1;
                        q[++tl]=j;
                    }
                }if(x==t)return 1;
            }return 0;
        }
        inline int dfs(int x,int in){
            if(!vis1[x]||!vis2[x])return 0;
            if(x==t)return in;
            int rest=in,go;
            for(int i=head[x],j;i;head[x]=i=e[i].next)if(e[i].w&&e[i].fl<3){
                if(dis[j=e[i].to]==dis[x]+1){
                    go=dfs(j,min(rest,e[i].w));
                    if(go)rest-=go,e[i].w-=go,e[i^1].w+=go;
                    else dis[j]=0;
                }if(!rest)break;
            }return in-rest;
        }
        inline int dinic(){
            int ans=0;
            while(bfs())ans+=dfs(s,inf);
            return ans;
        }
    }
    inline short main(){
        //file();
        n=read(),m=read();
        F(i,1,m){
            int x=read()+1,y=read()+1,z=read();
            add(x,y,z),e[co].fl=1,add(y,x,inf),e[co].fl=2;
            add(y,x,z),e[co].fl=3;
        }s=1,t=n,memcpy(Hd,head,sizeof(int)*(n+1));
        pc::bfs(s,vis1,1),pc::bfs(t,vis2,3);
        int x=dinic::dinic();
        pi(x>=inf/2?-1:x);
        return 0;
    }
}
signed main(){return EMT::main();}

收藏家

考虑网络流。
对于m个关系把人拆成好几轮,自己和自己连\(a_i\)的边权,由于可以互相交换,两两相互连边权是1的边。
然后从1向汇点连\(a_1\)的边,源点和所有点连\(1\)的边权,最后跑出的就是答案。

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);}//freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=1e5+10,inf=0x3f3f3f3f;
    int head[N],co,cnt,s,t,Hd[N],n,m,a[N];
    std::vector<int>id[N];
    struct node{int next,to,w;}e[N<<1];
    inline void add(int next,int to,int w){e[++co]={head[next],to,w},head[next]=co;}
    namespace dinic{
        int dis[N];
        inline bool bfs(){
            static int q[N],hd,tl;
            memset(dis,0x3f,sizeof(int)*(cnt+1));dis[s]=0;
            memcpy(head,Hd,sizeof(int)*(cnt+1));q[hd=tl=1]=s;
            while(hd<=tl){
                int x=q[hd++];
                for(int i=head[x],j;i;i=e[i].next)if(e[i].w)
                    if(dis[j=e[i].to]>dis[x]+1){
                        dis[j]=dis[x]+1;
                        q[++tl]=j;
                    }
                if(x==t)return 1;
            }return 0;
        }
        inline int dfs(int x,int in){
            if(x==t)return in;
            int rest=in,go;
            for(int i=head[x],j;i;head[x]=i=e[i].next)if(e[i].w){
                if(dis[j=e[i].to]==dis[x]+1){
                    go=dfs(j,min(e[i].w,rest));
                    if(go)rest-=go,e[i].w-=go,e[i^1].w+=go;
                    else dis[j]=0;
                }if(!rest)break;
            }return in-rest;
        }
        inline int dinic(){
            int ans=0;memcpy(Hd,head,sizeof(int)*(cnt+1));
            while(bfs())ans+=dfs(s,inf);return ans;
        }
    }
    inline void init(){memset(head,0,sizeof(int)*(cnt+1));co=1;}
    int dbg;
    inline void work(){
        init();
        n=cnt=read(),m=read();s=++cnt,t=++cnt;
        F(i,1,n)a[i]=read();
        F(i,1,n)id[i].clear(),id[i].push_back(i),add(s,i,1),add(i,s,0);
        F(i,1,m){
            int x=read(),y=read();
            id[x].push_back(++cnt);
            int lst=id[x][id[x].size()-2];
            add(lst,cnt,a[x]),add(cnt,lst,0);
            id[y].push_back(++cnt);
            lst=id[y][id[y].size()-2];
            add(lst,cnt,a[y]),add(cnt,lst,0);
            add(cnt-1,cnt,1),add(cnt,cnt-1,1);
        }add(id[1][id[1].size()-1],t,a[1]),add(t,id[1][id[1].size()-1],0);//if(++dbg==2)exit(0);
        pi(dinic::dinic());pn();
        std::cerr<<co<<std::endl;
    }
    inline short main(){
        //file();
        int T=read();
        while(T--)work();
        return 0;
    }
}
signed main(){return EMT::main();}

旅行

01分数规划。发现一个点的出边删不删和别的点无关,于是可以对于每个点求出自己的最大值。对于每个点二分自己的f值然后用网络流check,判断条件就是最大权闭合子图权值能不能是正数,最后1的答案就是答案。

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline db max(db a,db b){return a>b?a:b;}inline db min(db a,db b){return a<b?a:b;}
    inline void pi(db x){pf("%.10lf ",x);}inline void pn(){pf("\n");}
    const int N=3050;const db eps=1e-6,inf=1e9;
    int s,t,cnt,n,m,k;db f[N];
    namespace del{
        int head[N],co;
        struct node{int next,to;}e[N];
        inline void add(int next,int to){e[++co]={head[next],to},head[next]=co;}
    }
    namespace edg{
        int rfl[N][N];//rfl_{i,j} j对于i是第几个出边
        int a[N][N];//a_{i,j} i第j个出边是谁
        int head[N],co;
        struct node{int next,to;}e[N];
        inline void add(int next,int to){e[++co]={head[next],to},head[next]=co;a[next][rfl[next][co]=++a[next][0]]=co;}
    }
    namespace dinic{
        int dis[N*10],head[N*10],co,Hd[N*10];
        struct node{int next,to;db w;}e[N*10];
        inline void init(){memset(head,0,sizeof(int)*(cnt+1));co=1;}
        inline void add(int next,int to,db w){e[++co]={head[next],to,w},head[next]=co;}
        inline bool bfs(){
            static int q[N],hd,tl;
            memset(dis,0x3f,sizeof(int)*(cnt+1));dis[s]=0;
            memcpy(head,Hd,sizeof(int)*(cnt+1));q[hd=tl=1]=s;
            while(hd<=tl){
                int x=q[hd++];
                for(int i=head[x],j;i;i=e[i].next)if(e[i].w>eps){
                    if(dis[j=e[i].to]>dis[x]+1){
                        dis[j]=dis[x]+1;
                        q[++tl]=j;
                    }
                }if(x==t)return 1;
            }return 0;
        }
        inline db dfs(int x,db in){
            if(x==t)return in;
            db rest=in,go;
            for(int i=head[x],j;i;head[x]=i=e[i].next)if(e[i].w>eps){
                if(dis[j=e[i].to]==dis[x]+1){
                    go=dfs(j,min(e[i].w,rest));
                    if(go>eps)e[i].w-=go,e[i^1].w+=go,rest-=go;
                    else dis[j]=0;
                }if(!rest)break;
            }return in-rest;
        }
        inline db dinic(){
            db ans=0;memcpy(Hd,head,sizeof(int)*(cnt+1));
            while(bfs())ans+=dfs(s,inf);return ans;
        }
    }
    int id[N];
    inline int getid(int x){
        return id[x]?id[x]:id[x]=++cnt;
    }
    inline bool check(int k,db mid){
        dinic::init();cnt=0;s=++cnt,t=++cnt;db ans=0;
        for(int i=edg::head[k];i;i=edg::e[i].next){
            int x=getid(i);
            if(f[edg::e[i].to]>=mid)dinic::add(s,x,f[edg::e[i].to]-mid),ans+=f[edg::e[i].to]-mid,dinic::add(x,s,0);
            else dinic::add(x,t,mid-f[edg::e[i].to]),dinic::add(t,x,0);
            for(int j=del::head[i];j;j=del::e[j].next){
                int y=getid(del::e[j].to);
                dinic::add(x,y,inf),dinic::add(y,x,0);
            }
        }ans-=dinic::dinic();
        for(int i=edg::head[k];i;i=edg::e[i].next)id[i]=0;
        return ans>eps;
    }
    inline void getans(int k){
        if(!edg::a[k][0])return;if(f[k])return;
        for(int i=edg::head[k];i;i=edg::e[i].next)
            getans(edg::e[i].to);
        db l=0,r=m,ans=0;
        while(r-l>=1e-4){
            db mid=(l+r)/2.0;
            if(check(k,mid))l=mid,ans=mid;
            else r=mid;
        }return f[k]=ans+1,void();
    }
    inline short main(){
        //file();
        n=read(),m=read(),k=read();
        F(i,1,m){
            int x=read(),y=read();
            edg::add(x,y);
        }
        F(i,1,k){
            int x=read(),y=read();
            del::add(x,y);
        }
        getans(1);
        pi(f[1]);
        return 0;
    }
}
signed main(){return EMT::main();}

字符串

由于询问带有区间,于是考虑在主席树上做,在主席树上维护出现了至少两次的倒数第二次右端点、长度最大值,查询时查[l,r]内最大长度,[1,l]内最大右端点-l,取个max即可。然后由于要动态插入所以要LCT维护SAM的后缀树,那么一操作就是access把祖先的last修改,二操作就是如上的询问即可,考虑修改时只需要先改链最上面的点即可,再改时下传就行。

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=2e5+10;
    int rt[N],n,m;char s[N];
    namespace LCT{int fa[N],son[N][2],end[N];}
    namespace SAM{struct trie{int son[26],fa,len;}t[N];int last=1,tot=1;}
    struct seg{
        int tot,mx[N*60][2],ls[N*60],rs[N*60],n;
        inline void insert(int x,int &y,int l,int r,int v,int val[2]){
            if(!y)y=++tot,ls[y]=ls[x],rs[y]=rs[x],mx[y][0]=mx[x][0],mx[y][1]=mx[x][1];
            mx[y][0]=max(mx[y][0],val[0]),mx[y][1]=max(mx[y][1],val[1]);
            if(l==r)return;
            int mid=(l+r)>>1;
            if(v<=mid){
                if(ls[x]==ls[y])ls[y]=0;
                insert(ls[x],ls[y],l,mid,v,val);
            }else{
                if(rs[x]==rs[y])rs[y]=0;
                insert(rs[x],rs[y],mid+1,r,v,val);
            }
        }
        inline int ask(int x,int l,int r,int ql,int qr,int tp){
            if(!x||(l>=ql&&r<=qr))return mx[x][tp];
            int mid=(l+r)>>1;
            if(qr<=mid)return ask(ls[x],l,mid,ql,qr,tp);
            if(ql>mid)return ask(rs[x],mid+1,r,ql,qr,tp);
            return max(ask(ls[x],l,mid,ql,mid,tp),ask(rs[x],mid+1,r,mid+1,qr,tp));
        }
        inline void insert(int now,int y,int len){
            // pi(now);pi(y);pi(len);pn();
            if(!rt[y])rt[y]=rt[y-1];
            if(!len)return;
            if(rt[y]==rt[y-1])rt[y]=0;
            int val[2]={len,now};
            insert(rt[y-1],rt[y],1,n,now-len+1,val);
        }
        inline int ask(int l,int r){
            int x=ask(rt[r],1,n,l,n,0),y=ask(rt[r],1,n,1,l,1);
            return max(x,y-l+1);
        }
    }segm;
    namespace LCT{
        inline bool nroot(int x){
            return son[fa[x]][1]==x||son[fa[x]][0]==x;
        }
        inline void res(int x){
            int y=fa[x],z=fa[y],k=son[y][1]==x,w=son[x][!k];
            if(nroot(y))son[z][son[z][1]==y]=x;
            fa[x]=z,fa[y]=x;if(w)fa[w]=y;son[y][k]=w,son[x][!k]=y;end[x]=end[y];
        }
        inline void splay(int x){
            while(nroot(x)){
                int y=fa[x],z=fa[y];
                if(nroot(y))res((son[y][1]==x)^(son[z][1]==y)?x:y);
                res(x);
            }
        }
        inline void access(int x,int en){
            int y=0,lst=0;
            for(;x;x=fa[y=x]){
                splay(x);end[son[x][1]]=end[x],son[x][1]=y;
                if(end[x]!=lst)
                    segm.insert(lst=end[x],en,SAM::t[x].len);
            }if(y&&en)end[y]=en;
        }
        inline void adleaf(int x,int FA){
            // pi(x);pi(FA);pn();
            fa[x]=FA;
            access(x,SAM::t[x].len);
            // pi(x);pi(end[x]);pi(FA);pi(end[FA]);pn();
        }
        inline void adfa(int x,int FA){
            splay(x);
            fa[son[FA][0]=son[x][0]]=FA;
            fa[son[x][0]=FA]=x;
        }
    }
    namespace SAM{
        #define son(x,v) t[x].son[v]
        #define fa(x) t[x].fa
        #define len(x) t[x].len
        inline void insert(int v){
            int p=last,np=last=++tot;len(np)=len(p)+1;
            for(;p&&!son(p,v);p=fa(p))son(p,v)=np;
            if(!p)fa(np)=1,LCT::adleaf(np,1);
            else{
                int q=son(p,v);
                if(len(q)==len(p)+1)fa(np)=q,LCT::adleaf(np,q);
                else{
                    int nq=++tot;t[nq]=t[q];
                    len(nq)=len(p)+1,fa(np)=fa(q)=nq;
                    LCT::adfa(q,nq),LCT::adleaf(np,nq);
                    for(;p&&son(p,v)==q;p=fa(p))son(p,v)=nq;
                }
            }//pi(np);pi(fa(np));pi(114514);pn();
        }
        #undef son
        #undef fa
        #undef len
    }
    inline short main(){
    //	file();
        scanf("%s",s+1);n=strlen(s+1);m=read();
        segm.n=n+m;F(i,1,n)SAM::insert(s[i]-'a');int lans=0;
        while(m--){
            // pf("Case:\n\n");
            int opt=read();
            if(opt==1){
                scanf("%s",s+(++n));s[n]=((int)s[n]-'a'+lans)%26+'a';
                SAM::insert(s[n]-'a');//if(!rt[n])rt[n]=rt[n-1];
            }else{
                int l=(read()-1+lans)%n+1,r=(read()-1+lans)%n+1;
                if(l>r)l^=r^=l^=r;//pi(l);pi(r);pn();
                pi(lans=segm.ask(l,r));pn();
            }
        }
        return 0;
    }
}
signed main(){return EMT::main();}

1.3

young

\(f(n,m)\)表示n个点权值\([0,2^m-1)\)的生成树总和,则答案就是\(\frac{f(n,m)}{2^{n+m}}\)
考虑如何求\(f(n,m)\),可以枚举第\(m\)位为1的个数,则有:

\[f(n,m)=\sum\limits_{i=0}^{n}\binom{n}{i}(2^{(n-i)(m-1)}\times f_{i,m-1}+2^{i(m-1)}\times f_{n-i,m-1}+g_{i,n-i,m-1}+[i>0\&\&i<n]2^{(n+1)(m-1)}) \]

其中\(g_{S,T,m}\)表示一边有\(S\)个点,一边有\(T\)个点,点权是\([0,2^m-1)\)的所有情况的最小边的和。
式子大概就是枚举有几个1,然后从n里面选,没有1的所有情况与有一的乘起来,有一的所有情况和没1的乘起来,加上连接两块的最小边,如果确实是两个联通块再加上所有可能的边权。
然后考虑求\(g(S,T,m)\),有:

\[g(S,T,m)=\sum\limits_{k=0}^{2^m-1}p(S,T,m,k) \]

其中\(p(S,T,m,k)\)表示一边有S个点一边有T个点点权是\([0,2^m-1)\),最小边>=k的方案数,考虑p如何求。依然是枚举S里面有几个1,T里面有几个1,分成S0,S1,T0,T1,如果能异或出来0,即\(S0>0\&\&T0>0\)\(S1>0\&\&T1>0\)就递归求这两个的P,否则只能求剩下的P,把k减去\(2^{m-1}\).

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int mod=258280327;
    int f[55][55],g[55][55][12],p[55][55][12][1<<10],jc[55],inv[55],n,m,bin[1500];
    inline int ksm(int a,int b){
        int ans=1;
        while(b){
            if(b&1)ans=1ll*a*ans%mod;
            a=1ll*a*a%mod;b>>=1;
        }return ans;
    }
    inline int C(int n,int m){return 1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;}
    inline int getp(int S,int T,int m,int k){
        if(k<=0)return bin[(S+T)*m];
        if(!m&&k<=0)return 1;
        if((1<<m)-1<k)return 0;
        if(!S||!T)return bin[(S+T)*m];
        if(p[S][T][m][k])return p[S][T][m][k];
        int ans=0;
        F(i,0,S)F(j,0,T){
            int S0=i,S1=S-i,T0=j,T1=T-j;
            if((S0&&T0)||(S1&&T1)){
                ans+=1ll*C(S,S0)*C(T,T0)%mod*getp(S0,T0,m-1,k)%mod*getp(S1,T1,m-1,k)%mod,
                ans-=ans>=mod?mod:0;
            }else{
                if(S0&&T1)ans+=getp(S0,T1,m-1,k-(1<<(m-1))),ans-=ans>=mod?mod:0;
                if(S1&&T0)ans+=getp(S1,T0,m-1,k-(1<<(m-1))),ans-=ans>=mod?mod:0;
            }
        }
        return p[S][T][m][k]=ans;
    }
    inline int getg(int S,int T,int m){
        if(!S||!T||!m)return 0;
        if(g[S][T][m])return g[S][T][m];
        int ans=0;
        F(k,1,bin[m]-1)ans+=getp(S,T,m,k),ans-=ans>=mod?mod:0;
        return g[S][T][m]=ans;
    }
    inline int getf(int n,int m){
        if(n<=1||!m)return 0;
        if(f[n][m])return f[n][m];
        int ans=0;
        F(i,0,n)
        ans+=1ll*C(n,i)*((1ll*bin[(n-i)*(m-1)]*getf(i,m-1)%mod+1ll*bin[i*(m-1)]*getf(n-i,m-1)%mod+getg(i,n-i,m-1)+((i>0&&i<n)?bin[(n+1)*(m-1)]:0))%mod)%mod,
        ans-=ans>=mod?mod:0;
        return f[n][m]=ans;
    }
    inline short main(){
        //file();
        n=read(),m=read();
        jc[0]=inv[0]=inv[1]=bin[0]=1;
        F(i,1,n*m+1)bin[i]=bin[i-1]<<1,bin[i]-=bin[i]>=mod?mod:0;
        F(i,1,n)jc[i]=1ll*jc[i-1]*i%mod;
        inv[n]=ksm(jc[n],mod-2);
        D(i,n-1,2)inv[i]=1ll*inv[i+1]*(i+1)%mod;
        pi(1ll*getf(n,m)*ksm(bin[n*m],mod-2)%mod);
        return 0;
    }
}
signed main(){return EMT::main();}

Simple

\(f(n)\)表示n位数满足条件的个数,则答案就是:
\(\sum\limits_{i=1}^ni^2f(i)\)
考虑\(f(n)\)怎么求,首先循环节<n的一定不满足,考虑不循环的数字。设\(F(n)=\sum\limits_{d|n}f(n)\)
则有:

\[f(n)=\frac{\sum\limits_{d|n}10^{\frac{n}{d}}\mu(d)}{n} \]

带入到原式里面就是:

\[\sum\limits_{i=1}^ni\sum\limits_{d|i}10^{\frac{i}{d}}\mu(d)=\sum\limits_{d=1}^nd\mu(d)\sum\limits_{i=1}^{\frac{n}{d}}i10^i \]

\(g(n)=\sum\limits_{i=1}^{n}i\mu(i)\),设G有:

\[G(n)=\sum\limits_{i=1}^{n}i\sum\limits_{d|i}\mu(d)=\sum\limits_{d=1}^{n}d\mu(d)(\frac{n}{d}\times (\frac{n}{d}+1)/2)=\sum\limits_{d=1}^ndg(\frac{n}{d}) \]

显然有G等于1,于是有:

\[g(n)=1-\sum\limits_{d=2}^{n}dg(\frac{n}{d}) \]

于是杜教筛即可。

Code
#include<unordered_map>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define int long long
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%lld ",x);}inline void pn(){pf("\n");}
    const int mod=258280327,N=5e6+10;
    inline int ksm(int a,int b){int ans=1;while(b){if(b&1)ans=a*ans%mod;a=a*a%mod;b>>=1;}return ans;}
    const int inv81=ksm(81,mod-2),inv2=(mod+1)>>1;int n,mu[N],prime[N],co,pre[N];bool vis[N];
    inline int clac(int n){return ((9*n-1)%mod*ksm(10,n)%mod+1)*10%mod*inv81%mod;}
    std::unordered_map<int,int>mp;
    inline void init(){
        mu[1]=1;
        F(i,2,5000000){
            if(!vis[i])prime[++co]=i,mu[i]=mod-1;
            F(j,1,co){
                if(prime[j]*i>=N)break;
                vis[prime[j]*i]=1;
                if(i%prime[j]==0)break;
                if(mu[i])mu[prime[j]*i]=mod-mu[i];
            }
        }
        F(i,1,5000000)pre[i]=pre[i-1]+i*mu[i]%mod,pre[i]-=pre[i]>=mod?mod:0;
    }
    inline int get(int n){
        if(n<=5000000)return pre[n];
        if(mp.find(n)!=mp.end())return mp[n];
        int ans=0;
        for(int l=2,r;l<=n;l=r+1){
            r=n/(n/l);
            ans+=(l+r)%mod*((r-l+1)%mod)%mod*inv2%mod*get(n/l)%mod,
            ans-=ans>=mod?mod:0;
        }return mp[n]=mod+1-ans;
    }
    inline short main(){
        //file();
        n=read();int ans=0;init();
        for(int l=1,r;l<=n;l=r+1){
            r=n/(n/l);
            ans+=(clac(r)-clac(l-1)+mod)%mod*get(n/l)%mod;
            ans-=ans>=mod?mod:0;
        }pi(ans);
        return 0;
    }
}
signed main(){return EMT::main();}

mate

考虑枚举某一个方向走了几步,则别的都能算出来了。答案就是:

\[\sum\limits_{a=l}^{r}\frac{n!}{a!b!c!d!} \]

多重集排列方案数相加。其中lr是一个方向走的步数的上界,abcd分别是各个方向走了几步。
模数不好处理,于是我们可以把模数分成\(\prod p_i^{k_i}\)然后最后用中国剩余定理合并。
具体的话,对于每一个p,我们把阶乘中的所有p拿出来,用exgcd求逆元,最后把p再乘回去即可。

Code
#include<cassert>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
namespace excrt{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    #define int __int128
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline int gcd(int a,int b){return b?gcd(b,a%b):a;}
    inline void exgcd(int a,int b,int &x,int &y){if(!b){x=1,y=0;return;}exgcd(b,a%b,x,y);int z=x;x=y,y=z-(a/b)*y;}
    int n,nowa,nowb,newa,newb,x,y;
    inline int abs(int x){return x<0?-x:x;}
    inline void merge(){
        int p1=nowb,p2=newb;
        exgcd(p1,p2,x,y);x*=(newa-nowa),y*=(newa-nowa);
        int lcm=nowb*newb;
        nowa=((nowa+x*nowb)%lcm+lcm)%lcm;
        nowb=lcm;
    }
    #undef int
}
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    #define int long long
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);}//freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%lld ",x);}inline void pn(){pf("\n");}
    const int N=2e6+10;
    int n,mod,x,y,part[N],pk[N];
    inline int abs(int x){return x<0?-x:x;}
    inline void depart(){
        int sq=sqrt(mod),now=mod;
        for(int i=2;i<=sq;i++)if(now%i==0){
            part[++part[0]]=i;pk[part[0]]=1;
            while(now%i==0)pk[part[0]]*=i,now/=i;
        }if(now>1)part[++part[0]]=now,pk[part[0]]=now;
    }
    inline int ksm(int a,int b,int mod){
        int ans=1;
        while(b){
            if(b&1)ans=1ll*a*ans%mod;
            a=1ll*a*a%mod;b>>=1;
        }return ans;
    }
    struct node{
        int jc[N],cnt[N];
        inline void exgcd(int a,int b,int &x,int &y){
            if(!b){x=1,y=0;return;}
            exgcd(b,a%b,x,y);
            int z=x;x=y,y=z-(a/b)*y;
        }
        inline int inv(int x,int mod){
            int ans,ee;
            exgcd(x,mod,ans,ee);
            ans=(ans%mod+mod)%mod;
            return ans;
        }
        int ans;
        inline void init(int p,int mod){
            jc[0]=1;
            F(i,1,n){
                int now=i;cnt[i]=cnt[i-1];
                while(now%p==0)cnt[i]++,now/=p;
                jc[i]=1ll*jc[i-1]*now%mod;
            }
        }
        inline void clac(int p,int mod,int a,int b,int c,int d){
            int pw=cnt[n]-(cnt[a]+cnt[b]+cnt[c]+cnt[d]);
            ans+=1ll*ksm(p,pw,mod)*jc[n]%mod*inv(jc[a],mod)%mod*inv(jc[b],mod)%mod*inv(jc[c],mod)%mod*inv(jc[d],mod)%mod,
            ans-=ans>=mod?mod:0;
        }
    }mat[20];
    inline int crt(){
        excrt::nowa=mat[1].ans,excrt::nowb=pk[1];
        F(i,2,part[0])excrt::newa=mat[i].ans,excrt::newb=pk[i],excrt::merge();
        return excrt::nowa;
    }
    inline short main(){
    //	file();
        n=read(),mod=read();depart();
        x=abs(read()),y=abs(read());
        if(n<x+y||((n-x-y)&1)){pi(0);return 0;}
        int l=x,r=(n+x-y)>>1;
        F(i,1,part[0])mat[i].init(part[i],pk[i]);
        F(a,l,r){
            int b=a-x,c=(n+x+y-a*2)>>1,d=c-y;
            F(i,1,part[0])mat[i].clac(part[i],pk[i],a,b,c,d);
        }pi(crt());
        return 0;
    }
}
signed main(){return EMT::main();}

猜拳游戏

先考虑求出每一场的胜率,设YYC赢面是R,妹子是P,则胜率为\(\frac{p}{p+r}\)
我们显然要让胜率最大,即让\(\frac{p}{r}\)最大,于是可以二分答案,进行01分数规划。
具体check就是倒着dp,设\(f_{n+1,i}[i>0]=1\),\(f_{n+1,i}[i<0]=-mid\)
转移的话就是枚举这一局出什么取个max即可。
最后看\(f_{1,0}>=0\)就行了。
求出来之后就有胜率\(p\)了,然后可以高斯消元设\(a_i\)表示如果妹子赢,比YYC多赢i局的概率。则有\(a_i=a_{i+1}*p+a_{i-1}*(1-p)\),\(a_m1=1\)然后高斯消元出\(f_0\)即可。

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=1100;const db eps=1e-9;
    int n,m1,m2;
    db p[N][3],f[N][N<<1];
    inline int get(int x){return !x?1:(x==1?2:0);}//克星
    inline int turn(int x){return x+n+1;}
    inline int Turn(int x){return x+m2+1;}
    inline bool check(db mid){
        memset(f[n+1],0,sizeof(f[n+1]));
        F(i,1,n)f[n+1][turn(i)]=1;
        F(i,-n,-1)f[n+1][turn(i)]=-mid;
        D(i,n,1){
            F(j,-n,n){
                db tmp[3]={0,0,0},mx=-1e9;
                F(k,0,2){
                    tmp[k]=f[i+1][turn(j-1)]*p[i][get(k)]+f[i+1][turn(j+1)]*p[i][get(get(k))]+f[i+1][turn(j)]*p[i][k];
                    if(mx<tmp[k])mx=tmp[k];
                }f[i][turn(j)]=mx;
            }
        }return f[1][turn(0)]>=0;
    }
    db a[N<<1][N<<1],ans[N<<1];
    inline void gauss(int n){
        F(i,1,n){
            if(fabs(a[i][i])<eps){
                int key=i,mx=fabs(a[i][i]);
                F(j,i+1,n)if(fabs(a[j][i])>mx)mx=fabs(a[j][i]),key=j;
                std::swap(a[i],a[key]);
            }
            D(j,n,i)a[i][j]/=a[i][i];
            F(j,i+1,n)D(k,n,i)a[j][k]-=a[i][k]*a[j][i];
        }ans[n]=a[n][n+1];
        D(i,n-1,1){
            ans[i]=a[i][n+1];
            F(j,i+1,n)
            ans[i]-=ans[j]*a[i][j];
        }
    }
    inline short main(){
        //file();
        while(1){
            n=read(),m1=read(),m2=read();
            if(!n&&!m1&&!m2)return 0;
            F(i,1,n)F(j,0,2)p[i][j]=1.0*read()/100;
            db l=0,r=1e7,Ans=0;
            while(r-l>eps){
                db mid=(l+r)/2.0;
                if(check(mid))l=Ans=mid;
                else r=mid;
            }db q=1.0/(1.0/Ans+1.0);
            memset(a,0,sizeof(a));
            memset(ans,0,sizeof(ans));
            F(i,-m2,m1)a[Turn(i)][Turn(i)]=1;
            a[Turn(m1)][Turn(m1)+1]=1;
            F(i,-m2+1,m1-1){
                a[Turn(i)][Turn(i-1)]=q-1;
                a[Turn(i)][Turn(i+1)]=-q;
            }
            gauss(Turn(m1));pf("%.5lf\n",ans[Turn(0)]);
        }
        return 0;
    }
}
signed main(){return EMT::main();}

B君的回忆

注意到g在不同模数下都有循环节,于是考虑bsgs求出这个循环节,具体可以把mod拆成质数,然后对质数的循环节求lcm,让下一层递归时用这个求出的循环节,然后加上玄学先check mod+1,mod-1,mod可以很快地求出解。然后就是简单的矩阵快速幂了。

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
namespace EMT{
    typedef long long ll;typedef double db;typedef unsigned long long ull;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline ll min(ll a,ll b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    int c[3][3],A,B,n,k,mod;
    std::map<int,int>mp;
    struct matrix{
        int a[3][3];
        matrix(int A=1,int b=0,int c=0,int d=1){a[1][1]=A,a[1][2]=b,a[2][1]=c,a[2][2]=d;}
        inline void mul(int b[3][3],int mod){
            c[1][1]=(1ll*a[1][1]*b[1][1]+1ll*a[1][2]*b[2][1])%mod,
            c[1][2]=(1ll*a[1][1]*b[1][2]+1ll*a[1][2]*b[2][2])%mod,
            c[2][1]=(1ll*a[2][1]*b[1][1]+1ll*a[2][2]*b[2][1])%mod,
            c[2][2]=(1ll*a[2][1]*b[1][2]+1ll*a[2][2]*b[2][2])%mod;
            a[1][1]=c[1][1],a[1][2]=c[1][2],a[2][1]=c[2][1],a[2][2]=c[2][2];
        }
        bool operator <(const matrix &x)const{
            for(int i=1;i<=2;i++)for(int j=1;j<=2;j++)
                if(a[i][j]!=x.a[i][j])return a[i][j]<x.a[i][j];
            return 0;
        }
    };
    inline bool judge(matrix x){
        return x.a[1][1]==1&&x.a[2][2]==1&&!x.a[1][2]&&!x.a[2][1];
    }
    inline matrix ksm(int mod,int n){
        matrix b(3,1,mod-1,0),a;
        while(n){
            if(n&1)a.mul(b.a,mod);
            b.mul(b.a,mod);n>>=1;
        }return a;
    }
    inline int bsgs(int mod){
        if(mp.count(mod))return mp[mod];
        if(judge(ksm(mod,mod+1)))return mp[mod]=mod+1;
        if(judge(ksm(mod,mod-1)))return mp[mod]=mod-1;
        if(judge(ksm(mod,mod)))return mp[mod]=mod;
        std::map<matrix,int>hs;
        int m=sqrt(min(2ll*mod,2e9));
        matrix a(3,1,mod-1,0),b;
        F(i,0,m-1){
            hs[b]=i;
            b.mul(a.a,mod);
        }matrix aa=a,base;

        int mm=m;
        while(mm){
            if(mm&1)base.mul(aa.a,mod);
            aa.mul(aa.a,mod),mm>>=1;
        }a=base;
        F(i,1,m+1){
            int k=hs.count(a);
            if(k==0){
                a.mul(base.a,mod);
                continue;
            }return	mp[mod]=i*m-hs[a];
        }return -1;
    }
    int d[3];
    inline void mul(int a[3],int b[3][3],int mod){
        d[1]=(1ll*a[1]*b[1][1]+1ll*a[2]*b[2][1])%mod;
        d[2]=(1ll*a[1]*b[1][2]+1ll*a[2]*b[2][2])%mod;
        a[1]=d[1],a[2]=d[2];
    }
    inline int getg(int n,int mod){
        int w[3]={0,B,A};n--;
        if(n==-1)return A;
        matrix b(3,1,mod-1,0),a;
        while(n){
            if(n&1)a.mul(b.a,mod);
            b.mul(b.a,mod);n>>=1;
        }mul(w,a.a,mod);return w[1];
    }
    inline int gcd(int a,int b){return b?gcd(b,a%b):a;}
    inline int lcm(int a,int b){return a/gcd(a,b)*b;}
    inline int getf(int n,int k,int mod){
        if(!k)return n;int v=1;if(k>1){
            int now=mod;
            for(int i=2;i<=now/i;i++)if(now%i==0){
                int val=bsgs(i);now/=i;
                while(now%i==0)val*=i,now/=i;
                v=lcm(v,val);
            }if(now>1)v=lcm(v,bsgs(now));
        }
        return getg(getf(n,k-1,v),mod);
    }
    inline short main(){
    //	file();
        int T=read();
        while(T--){
            A=read(),B=read(),n=read(),k=read(),mod=read();
            pi(getf(n,k,mod)),pn();
        }
        return 0;
    }
}
signed main(){return EMT::main();}

小 H 爱染色

题解很清楚,不想多说了。。。

然后先把最后一堆卷了,然后再和k卷一下就好了。

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=4e6+10,mod=998244353,g=3,gi=332748118;
    int n,m,f[N],jc[N],inv[N];
    inline int ksm(int a,int b){
        int ans=1;
        while(b){
            if(b&1)ans=1ll*a*ans%mod;
            a=1ll*a*a%mod;b>>=1;
        }return ans;
    }
    int it1[N<<1],it2[N<<1],itk[N<<1];
    namespace NTT{
        int lim=1,l,r[N<<1];
        inline void getlim(int n){
            lim=1,l=0;
            while(lim<=n)lim<<=1,l++;
            F(i,0,lim)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
        }
        inline void ntt(int *a,int tp){
            F(i,0,lim)if(i<r[i])std::swap(a[i],a[r[i]]);
            for(int mid=1;mid<lim;mid<<=1){
                int wn=ksm(tp==1?g:gi,(mod-1)/(mid<<1));
                for(int j=0;j<lim;j+=(mid<<1)){
                    int w=1;
                    for(int k=0;k<mid;k++,w=1ll*w*wn%mod){
                        int x=a[j+k],y=1ll*a[j+mid+k]*w%mod;
                        a[j+k]=(x+y)%mod;
                        a[j+mid+k]=(x-y+mod)%mod;
                    }
                }
            }
            if(tp==-1){
                int inv=ksm(lim,mod-2);
                F(i,0,lim)a[i]=1ll*a[i]*inv%mod;
            }
        }
        inline void clac(int *a,int *b,int n){
            getlim(n);
            ntt(a,1),ntt(b,1);
            F(i,0,lim)a[i]=1ll*a[i]*b[i]%mod;
            ntt(a,-1);
        }
    }
    inline int C(int n,int m){
        if(n<m||n<0||m<0)return 0;
        return 1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;
    }
    int nd[N<<2];
    inline void init(){
        nd[0]=1;
        F(i,1,m*3)nd[i]=1ll*nd[i-1]*(n-i+1)%mod;
    }
    inline int Cn(int m){return 1ll*nd[m]*inv[m]%mod;}
    inline short main(){
    //	file();
        n=read(),m=read();init();
        F(i,0,m)f[i]=read();
        jc[0]=inv[0]=inv[1]=1;
        F(i,1,m*3)jc[i]=1ll*jc[i-1]*i%mod;
        inv[m*3]=ksm(jc[m*3],mod-2);
        D(i,(m*3)-1,2)inv[i]=1ll*inv[i+1]*(i+1)%mod;
        F(i,0,m)it1[i]=1ll*(i&1?mod-1:1)*inv[i]%mod;
        F(i,0,m)it2[i]=1ll*f[i]*inv[i]%mod;
        NTT::clac(it1,it2,m<<1);
        F(i,0,m)it1[i]=1ll*it1[i]*jc[i]%mod;
        F(i,m+1,NTT::lim)it1[i]=0;
        F(i,m,m<<1)itk[i]=1ll*C(i,m)*C(m,(m<<1)-i)%mod;
        NTT::clac(it1,itk,m*3);int ans=0;
        F(i,m,m*3)ans+=1ll*it1[i]*Cn(i)%mod,ans-=ans>=mod?mod:0;
        pi(ans);
        return 0;
    }
}
signed main(){return EMT::main();}

解方程

因为不会exlucas寄了。
先保证都是正整数,那么我们先从m个1里面拿走n个1给每个数。
第二个限制比较好说,我们从剩下的m-n个1里面再分给这些数,分到所有数都满足限制。
那么剩下的就是第一个限制,问题转化为:
现在有m个相同的小球扔到n个不同的盒子里面,盒子可以空,并且前n1个盒子能放的球不能超过一定数量,问方案数。
正难则反,考虑设\(f_i\)表示前n1个盒子里面至少\(i\)个不满足条件的方案数。具体就是爆搜,钦定哪几个不满足,从m个1里面拿出来\(\sum a_i+1\)个分给这些数,剩下的1就转化成了:
现在有m个相同的小球扔到n个不同的盒子里面,盒子可以空,问方案数。
然后考场上忘了这个是啥组合数了,打表发现是\(\binom{n+m-1}{n-1}\),然后累加到对应的\(f_i\)中即可。
最后就是简单的自己容斥,由于模数不一定是质数,于是使用exlucas求解。

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace exlucas{
    #define int long long
    int m,n,p,co,c[100010],a[100010];
    int ksm(int a,int b,int p){
        int ans=1;
        while(b){
            if(b&1){ans=ans*a%p;}
            a=a*a%p;
            b>>=1;
        }return ans;
    }
    void exgcd(int a,int b,int &x,int &y){
        if(b==0){x=1,y=0;return;}
        exgcd(b,a%b,x,y);
        int xx=x;x=y;y=xx-y*(a/b);
    }
    int inv(int a,int p){
        int x,y;
        exgcd(a,p,x,y);
        return (x%p+p)%p;
    }
    int fac(int n,int p,int pk){
        if(!n)return 1;
        int ans=1;
        for(int i=1;i<pk;i++){
            if(i%p)ans=ans*i%pk;
        }ans=ksm(ans,n/pk,pk);
        for(int i=1;i<=n%pk;i++)if(i%p)ans=ans*i%pk;
        return ans*fac(n/p,p,pk)%pk;
    }
    int C(int n,int m,int p,int pk){
        if(n<m)return 0;int f1=fac(n,p,pk),f2=fac(m,p,pk),f3=fac(n-m,p,pk),cnt=0;
        for(int i=n;i;i/=p)cnt+=i/p;
        for(int i=m;i;i/=p)cnt-=i/p;
        for(int i=n-m;i;i/=p)cnt-=i/p;
        return f1*inv(f2,pk)%pk*inv(f3,pk)%pk*ksm(p,cnt,pk)%pk;
    }
    int crt(){
        int tot=1,ans=0;
        for(int i=1;i<=co;i++)tot*=c[i];
        for(int i=1;i<=co;i++)
            ans=(ans+a[i]*(tot/c[i])%tot*inv(tot/c[i],c[i])%tot)%tot;
        return ans;	
    }
    bool fl;
    int prime[100100];
    int exlucas(int n,int m,int p){
        if(!fl){
            int sq=sqrt(p);fl=1;
            for(int i=2;i<=sq&&p>1;i++){
                int tmp=1;
                while(p%i==0)p/=i,tmp*=i;
                if(tmp>1)a[++co]=C(n,m,i,tmp),c[co]=tmp,prime[co]=i;
            }
            if(p>1)a[++co]=C(n,m,p,p),c[co]=p,prime[co]=p;
        }else{
            for(int i=1;i<=co;i++)
                a[i]=C(n,m,prime[i],c[i]);
        }
        return crt();
    }
    #undef int
}
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    int pcnt,a[20],n,n1,n2,m,mod;
    int ans,st,f[20000];bool vis[20000];
    inline void check(){
        int cnt=0;
        F(i,1,n1)cnt+=vis[i];
        int now=m;
        F(i,1,n1)if(vis[i])now-=a[i]+1;
        if(now<0)return;
        f[cnt]+=exlucas::exlucas(now+n-1,n-1,mod),f[cnt]-=f[cnt]>=mod?mod:0;
    }
    inline void dfs(int k){
        if(k==n1+1){check();return;}
        vis[k]=1,dfs(k+1),
        vis[k]=0,dfs(k+1);
    }
    inline short main(){
        //file();
        int T=read();mod=read();
        while(T--){
            n=read(),n1=read(),n2=read(),m=read()-n;
            F(i,1,n1+n2)a[i]=read()-1;
            F(i,n1+1,n1+n2)m-=a[i];
            if(m<0){pi(0);pn();continue;}
            if(!m){pi(1);pn();continue;}
            memset(f,0,sizeof(f));
            dfs(1);
            ans=f[0],st=1;
            F(i,1,n1)st=mod-st,ans+=1ll*f[i]*st%mod,ans-=ans>=mod?mod:0;
            pi(ans);pn();
        }
        return 0;
    }
}
signed main(){return EMT::main();}

宇宙序列

先把a进行FWT卷积,然后设\(b_i=\sum\limits_{j=0}^{p} a_i^{2^j}\)
则把\(b_i\)再FWT回来就是答案了。
考虑找到\(2^i \pmod{10005}\)的循环节,然后算出每一个数对于上式的贡献,累加到b里即可。

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int mod=10007,inv2=(mod+1)>>1;
    int lim[5100],pw[mod][5010],to[mod];
    inline int ksm(int a,int b,int mod){
        int ans=1;
        while(b){
            if(b&1)ans=1ll*a*ans%mod;
            a=1ll*a*a%mod;b>>=1;
        }return ans;
    }
    int n,p,goal,pwn,a[1<<20],b[1<<20];
    inline void fwt(int *a,int tp){
        for(int mid=1;mid<pwn;mid<<=1)
            for(int j=0;j<pwn;j+=(mid<<1))
                for(int k=0;k<mid;k++){
                    int x=a[j+k],y=a[j+mid+k];
                    a[j+k]=(x+y)%mod;
                    a[j+mid+k]=(x-y+mod)%mod;
                    if(tp==-1)a[j+k]=1ll*a[j+k]*inv2%mod,a[j+mid+k]=1ll*a[j+mid+k]*inv2%mod;
                }
    }
    inline void getpw(int v){
        pw[v][0]=v;
        F(i,1,5002)pw[v][i]=1ll*pw[v][i-1]*pw[v][i-1]%mod;
    }
    inline short main(){
        //file();
        n=read(),p=read(),goal=read(),pwn=1<<n;
        F(i,0,pwn-1)a[i]=read();
        F(i,1,10006)getpw(i);
        fwt(a,1);
        int x=p/5002,y=p%5002;
        F(i,1,10006){
            int v=i;
            F(j,1,5002){
                v+=1ll*(x+(j<=y))*pw[i][j]%mod;
                v-=v>=mod?mod:0;
            }to[i]=v;
        }
        F(i,0,pwn-1)b[i]=to[a[i]];
        fwt(b,-1);
        pi(b[goal]);
        return 0;
    }
}
signed main(){return EMT::main();}

exp

\(f_{l,r}\)为消完\([l,r-1]\)还剩\(r\)没消的期望,\(g_{l,r}\)表示对应的概率,\(p_{l,r,k}\)表示消完\([l,r-1]\)并且最后一个消的是\(k\)的概率。
对于\(p_{l,r,k}\)\([l,k-1]\)\(lc\)个0,\([k+1,r]\)\(rc\)个0,有:

\[p_{l,r,k}=\binom{lc+rc}{lc}\times (\frac{k-l+1}{r-l+1})^{lc}\times g_{l,k} \times (\frac{r-k}{r-l+1})^{rc} \times g_{k+1,r} \times \frac{k-l+1}{r-l+1} \]

大概意义就是我肯定要从k左边选lc个k右边选rc个并且左边选到的概率是\(\frac{k-l+1}{r-l+1}\),右边概率是\(\frac{r-k}{r-l+1}\)然后加个次方。由于顺序随便所以乘上一个\(\binom{lc+rc}{lc}\),附带上左右两个区间的\(g\),然后最后一次钦定选一个k,选到的概率就是\(\frac{k-l+1}{r-l+1}\)
然后就有:

\[g_{l,r}=\sum\limits_{k=l}^r[a_k=0]p_{l,r,k} \]

\[f_{l,r}=\frac{1}{g_{l,r}}\sum\limits_{l=l}^{r-1}p_{l,r,k}\times (f_{l,k}+f_{k+1,r}+\frac{k-l}{2}) \]

其中f是个加权平均,g函数的式子显然,f的\(\frac{k-l}{2}\)是选到k的贡献。然后最后一次选到的那个数有\(\frac{n-1}{2}\)的贡献,最后答案枚举最后一次选到的数,答案是:

\[ans=\sum\limits_{i=1}^ng_{i,i+n-1}(f_{i,i+n-1}+\frac{n-1}{2}) \]

然后小判一手边界就行。

Code
#include<cassert>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=410;const db eps=1e-6;
    db f[N][N],g[N][N],C[N][N];bool visg[N][N],visf[N][N];
    inline db getp(int l,int r,int k);
    inline db getg(int l,int r);
    inline db getf(int l,int r);
    inline void init(){
        C[0][0]=1;
        F(i,1,400){
            C[i][0]=1;
            F(j,1,i)C[i][j]=C[i-1][j-1]+C[i-1][j];
        }
    }
    inline db ksm(db a,int b){db ans=1;while(b){if(b&1)ans*=a;a*=a;b>>=1;}return ans;}
    int pre[N],n;char s[N];
    inline db getg(int l,int r){
        if(pre[r-1]==pre[l-1]&&s[r]=='.')return 1;
        if(visg[l][r])return g[l][r];
        visg[l][r]=1;
        F(k,l,r-1)if(s[k]=='.')g[l][r]+=getp(l,r,k);
        // F(k,l,r-1)g[l][r]+=getp(l,r,k);
        return g[l][r];
    }
    inline db getf(int l,int r){
        if(pre[r-1]==pre[l-1]&&s[r]=='.')return 0;
        if(visf[l][r])return f[l][r];
        visf[l][r]=1;
        if(getg(l,r)<eps)return f[l][r]=0;
        F(k,l,r-1)if(s[k]=='.')f[l][r]+=getp(l,r,k)*(getf(l,k)+getf(k+1,r)+(db)(k-l)/2.0);
        // F(k,l,r-1)f[l][r]+=getp(l,r,k)*(getf(l,k)+getf(k+1,r)+(db)(k-l)/2.0);
        f[l][r]/=getg(l,r);
        return f[l][r];
    }
    inline db getp(int l,int r,int k){
        if(l>r)return 0;
        int lc=pre[k-1]-pre[l-1],rc=pre[r-1]-pre[k];
        return C[lc+rc][lc]*ksm((db)(k-l+1)/(db)(r-l+1),lc)*getg(l,k)*ksm((db)(r-k)/(db)(r-l+1),rc)*getg(k+1,r)*((db)(k-l+1)/(db)(r-l+1));
    }
    inline short main(){
        //file();
        int T=read();init();
        while(T--){
            memset(f,0,sizeof(f));
            memset(g,0,sizeof(g));
            memset(visg,0,sizeof(visg));
            memset(visf,0,sizeof(visf));
            scanf("%s",s+1);n=strlen(s+1);
            memcpy(s+n+1,s+1,sizeof(char)*n);
            F(i,1,n<<1)pre[i]=pre[i-1]+(s[i]=='.');
            db ans=0;
            F(i,1,n)ans+=getg(i,i+n-1)*(getf(i,i+n-1)+(db)(n-1)/2.0);
            pf("%.10lf\n",ans);
        }
        return 0;
    }
}
signed main(){return EMT::main();}

传统题

丢下题解就跑.jpg

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=3e5+10;
    int n,m,mod,mpw[N],mmpw[N],jc[N],inv1[N],inv2[N];
    inline int ksm(int a,int b){
        int ans=1;
        while(b){
            if(b&1)ans=1ll*a*ans%mod;
            a=1ll*a*a%mod;b>>=1;
        }return ans;
    }
    inline int C(int n,int m){
        if(n<m||n<0||m<0)return 0;
        return 1ll*jc[n]*inv2[m]%mod*inv2[n-m]%mod;
    }
    inline short main(){
        //file();
        n=read(),m=read(),mod=read();
        mpw[0]=1;mmpw[0]=jc[0]=inv1[0]=inv1[1]=inv2[0]=inv2[1]=1;
        F(i,1,n)mpw[i]=1ll*mpw[i-1]*m%mod;
        F(i,1,n)mmpw[i]=1ll*mmpw[i-1]*(m-1)%mod;
        F(i,2,n)inv1[i]=1ll*(mod-mod/i)*inv1[mod%i]%mod;
        F(i,1,n)jc[i]=1ll*jc[i-1]*i%mod;
        inv2[n]=ksm(jc[n],mod-2);
        D(i,n-1,2)inv2[i]=1ll*inv2[i+1]*(i+1)%mod;
        int ans=0;
        F(i,0,n-1){
            int lim=i?n/i:n;
            F(k,0,lim){
                if(n-i*k==0)continue;
                int fir=1ll*((k&1)?mod-1:1)*inv1[n-i*k]%mod*C(n-i*k,k)%mod;
                int sec=0;
                if(n-i*k-k>=0&&k-1>=0)sec=1ll*k*mpw[n-i*k-k]%mod*mmpw[k-1]%mod;
                int thi=0;
                if(n-i*k-k-1>=0)thi=1ll*mmpw[k]*(n-i*k-k)%mod*mpw[n-i*k-k-1]%mod;
                ans+=1ll*fir*(sec+thi)%mod,
                ans-=ans>=mod?mod:0;
            }
        }ans=1ll*ans*m%mod;ans=mod-ans;
        ans+=1ll*n*mpw[n]%mod;
        ans-=ans>=mod?mod:0;
        pi(ans);
        return 0;
    }
}
signed main(){return EMT::main();}

生成树

直接讲不好讲,先举个栗子。
我们都知道,矩阵树定理求出的行列式是所有生成树边权之积的加和。
比如现在有一颗4个点3条边的生成树,然后只有两种颜色那么它的边权构成可能有:
\(rrr\),\(rrg\),\(rgg\),\(ggg\)四种情况,那么我们令r值是1,g值分别是0,1,2,3就能得到一个方程组:

\[1\ rrr+0\ rrg+0\ rgg+0\ ggg=v_0 \]

\[1\ rrr+1\ rrg+1\ rgg+1\ ggg=v_1 \]

\[1\ rrr+2\ rrg+4\ rgg+8\ ggg=v_2 \]

\[1\ rrr+3\ rrg+9\ rgg+27\ ggg=v_3 \]

其中\(v_i\)可以通过矩阵树定理求出。
然后求解,得到的答案就是各种情况分别有多少种,然后累加到答案里面即可。
扩展到三个颜色也是一样的,随便取几个值就可以解出答案了。

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cassert>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=45,mod=1e9+7;
    inline void Mod(int &x,int v){x+=v,x-=x>=mod?mod:0;}
    int n,m,G,B,id[N][N][N],cnt,hv[N*N*N][4];
    inline int ksm(int a,int b){
        int ans=1;
        while(b){
            if(b&1)ans=1ll*a*ans%mod;
            a=1ll*a*a%mod;b>>=1;
        }return ans;
    }
    namespace gauss{
        int a[N*N][N*N],ans[N*N];
        inline void gauss(int n){
            F(i,1,n){
                if(!a[i][i]){
                    F(j,i+1,n)if(a[j][i]){
                        std::swap(a[i],a[j]);
                        break;
                    }
                }
                int inv=ksm(a[i][i],mod-2);
                F(j,i,n+1)a[i][j]=1ll*a[i][j]*inv%mod;
                F(j,i+1,n)if(a[j][i])
                    D(k,n+1,i)
                        a[j][k]-=1ll*a[i][k]*a[j][i]%mod,
                        a[j][k]+=a[j][k]<0?mod:0;
            }
            ans[n]=a[n][n+1];
            D(i,n-1,1){
                if(a[i][i])ans[i]=a[i][n+1];
                F(j,i+1,n)
                    ans[i]-=1ll*ans[j]*a[i][j]%mod,
                    ans[i]+=ans[i]<0?mod:0;
            }
        }
    }
    namespace matrix{
        int a[N][N];
        inline void init(){F(i,1,n)memset(a[i],0,sizeof(int)*(n+1));}
        inline void add(int x,int y,int v){Mod(a[x][x],v),Mod(a[y][y],v),Mod(a[x][y],mod-v),Mod(a[y][x],mod-v);}
        inline int getans(){
            int ans=1;
            F(i,1,n-1){
                if(!a[i][i]){
                    F(j,i+1,n-1)if(a[j][i]){
                        std::swap(a[i],a[j]);
                        ans=mod-ans;break;
                    }
                }if(!a[i][i])return 0;
                int inv=ksm(a[i][i],mod-2);
                F(j,i+1,n-1)if(a[j][i])
                    D(k,n-1,i)
                        a[j][k]-=1ll*a[i][k]*a[j][i]%mod*inv%mod,
                        a[j][k]+=a[j][k]<0?mod:0;
                ans=1ll*ans*a[i][i]%mod;
            }return ans;
        }
    }
    struct node{int from,to,w;}e[N*N*N*N];
    inline short main(){
        srand((unsigned long long)(new char));
        n=read(),m=read(),G=read(),B=read();int cntg=0,cntb=0,cntr=0;
        F(i,1,m)e[i].from=read(),e[i].to=read(),e[i].w=read(),cntr+=e[i].w==1,cntg+=e[i].w==2,cntb+=e[i].w==3;
        F(i,0,min(cntr,n-1))F(j,0,min(cntg,n-1))F(k,0,min(cntb,n-1)){
            if(i+j+k!=n-1)continue;
            id[i][j][k]=++cnt;
            hv[cnt][1]=i,hv[cnt][2]=j,hv[cnt][3]=k;
        }
        F(i,1,cnt){
            matrix::init();
            int v2=rand()%mod,v3=rand()%mod;
            F(j,1,cnt)gauss::a[i][j]=1ll*ksm(v2,hv[j][2])*ksm(v3,hv[j][3])%mod;
            F(j,1,m)matrix::add(e[j].from,e[j].to,e[j].w==1?1:(e[j].w==2?v2:v3));
            gauss::a[i][cnt+1]=matrix::getans();
        }
        gauss::gauss(cnt);
        int ans=0;
        F(i,1,cnt)if(hv[i][2]<=G&&hv[i][3]<=B)ans+=gauss::ans[i],ans-=ans>=mod?mod:0;
        pi(ans);
        return 0;
    }
}
signed main(){return EMT::main();}

最短路径

首先对于每个基环树环外的联通块,我们可以用点分治配合FFT求出它的解,然后问题就在于合并环上的点。
对于环上的点,可以分成|x1|x2|x3|x4|四个部分,其中我们先对(x1,x2),(x3,x4)进行分治求解,然后再对于(x1,x3),(x2,x4)分成走两条路分治求解,细节挺多的,不是很能讲出来。。。

Code
#include<cassert>
#include<vector>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using std::vector;
namespace EMT{
    typedef long long ll;typedef double db;typedef vector<int> vi;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=1e6+10,mod=998244353,g=3,gi=332748118,inv2=(mod+1)>>1;
    inline int ksm(int a,int b){
        int ans=1;
        while(b){
            if(b&1)ans=1ll*a*ans%mod;
            a=1ll*a*a%mod;b>>=1;
        }return ans;
    }
    int lim,r[N],l,ans[N];
    inline void ntt(vi &a,int tp){
        for(int i=0;i<lim;i++)if(i<r[i])std::swap(a[i],a[r[i]]);
        for(int mid=1;mid<lim;mid<<=1){
            int wn=ksm(tp==1?g:gi,(mod-1)/(mid<<1));
            for(int j=0;j<lim;j+=(mid<<1)){
                int w=1;
                for(int k=0;k<mid;k++,w=1ll*w*wn%mod){
                    int x=a[j+k],y=1ll*a[j+mid+k]*w%mod;
                    a[j+k]=(x+y)%mod;
                    a[j+mid+k]=(x-y+mod)%mod;
                }
            }
        }
        if(tp==-1){
            int inv=ksm(lim,mod-2);
            for(int i=0;i<lim;i++)a[i]=1ll*a[i]*inv%mod;
        }
    }
    inline vi mul(vi a,vi b){
        int n=a.size()+b.size()-1;
        if(n==-1)return a;
        lim=1,l=0;
        while(lim<=n)lim<<=1,l++;
        for(int i=0;i<lim;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
        a.resize(lim),b.resize(lim);
        ntt(a,1),ntt(b,1);
        for(int i=0;i<lim;i++)a[i]=1ll*a[i]*b[i]%mod;
        ntt(a,-1);a.resize(n);
        return a;
    }
    int n,k,head[N],co,rt[N],Rt,mx[N],siz[N],size,id[N];bool vis[N];
    struct node{int next,to;}e[N<<1];vi f[N];
    inline void add(int next,int to){e[++co]={head[next],to},head[next]=co;}
    inline void dfs(int k){
        vis[k]=1;id[++id[0]]=k;
        for(int i=head[k],j;i;i=e[i].next)if(rt[j=e[i].to]&&!vis[j])dfs(j);
    }
    inline void topo(){
        static int q[N],hd=1,tl,in[N];
        for(int i=1;i<=co;i+=2)in[e[i].to]++,in[e[i+1].to]++;
        F(i,1,n)if(in[i]==1)q[++tl]=i;
        while(hd<=tl){
            int x=q[hd++];
            for(int i=head[x],j;i;i=e[i].next)if(in[j=e[i].to]>1){
                in[j]--;if(in[j]==1)q[++tl]=j;
            }
        }
        F(i,1,n)if(in[i]>1)rt[i]=i;int st;
        F(i,1,n)if(rt[i]==i){q[hd=tl=1]=st=i;break;}
        dfs(st);
    }
    inline void findrt(int k,int fa){
        mx[k]=size-siz[k];
        for(int i=head[k],j;i;i=e[i].next)if(!vis[j=e[i].to]&&j!=fa){
            findrt(j,k),mx[k]=max(mx[k],siz[j]);
        }mx[k]=max(mx[k],size-siz[k]);
        if(mx[k]<mx[Rt])Rt=k;
    }
    inline void getdis(int k,int fa,int d,vi &g){
        siz[k]=1;
        if(g.size()<=d)g.push_back(1);else g[d]++;
        for(int i=head[k],j;i;i=e[i].next)if(!vis[j=e[i].to]&&j!=fa)
            getdis(j,k,d+1,g),siz[k]+=siz[j];
    }
    inline void solve(int k){
        vi f;vis[k]=1;
        for(int i=head[k],j;i;i=e[i].next)if(!vis[j=e[i].to]){
            vi g;
            getdis(j,k,0,g);
            if(f.size()<g.size())f.resize(g.size());
            for(int i=0;i<g.size();i++)f[i]+=g[i],f[i]-=f[i]>=mod?mod:0;
            g=mul(g,g);
            for(int i=0;i<g.size();i++)(ans[i+2]-=g[i])%=mod,ans[i+2]+=ans[i+2]<0?mod:0;
            mx[Rt=0]=siz[j],size=siz[j],
            findrt(j,k),solve(Rt);
        }
        for(int i=0;i<f.size();i++)ans[i+1]+=2ll*f[i]%mod,ans[i+1]-=ans[i+1]>=mod?mod:0;
        f=mul(f,f);
        for(int i=0;i<f.size();i++)ans[i+2]+=f[i],ans[i+2]-=ans[i+2]>=mod?mod:0;
    }
    inline void clac(int l1,int r1,int l2,int r2,int x){
        int la=0,lb=0;
        F(i,l1,r1)la=max(la,f[i].size()+r1-i+1);
        F(i,l2,r2)lb=max(lb,f[i].size()+i-l2+1);
        vi a,b;a.resize(la+1),b.resize(lb+1);
        F(i,l1,r1)for(int j=0;j<f[i].size();j++)(a[j+r1-i]+=f[i][j])%=mod;
        F(i,l2,r2)for(int j=0;j<f[i].size();j++)(b[j+i-l2]+=f[i][j])%=mod;
        a=mul(a,b);
        for(int i=0;i<a.size();i++)(ans[i+x]+=a[i])%=mod;
    }
    inline void solve1(int l,int r){
        if(l==r)return;
        int mid=(l+r)>>1;
        clac(l,mid,mid+1,r,1);
        solve1(l,mid),solve1(mid+1,r);
    }
    inline void solve2(int l1,int r1,int l2,int r2,int x){
        if(l1==r1){clac(l1,r1,l2,r2,x);return;}
        int mid1=(l1+r1)>>1,mid2=(l2+r2)>>1;
        clac(mid1+1,r1,l2,mid2,x);
        solve2(l1,mid1,l2,mid2,x+r1-mid1),
        solve2(mid1+1,r1,mid2+1,r2,x+mid1-l1+1);
    }
    inline short main(){
        n=read(),k=read()%(mod-1);bool fl=0;
        F(i,1,n){
            int x=read(),y=read();
            if(x==y)fl=1;
            else add(x,y),add(y,x);
        }
        if(fl){
            getdis(1,0,0,f[1]);
            mx[Rt=0]=siz[1],size=siz[1];
            findrt(1,0);
            solve(Rt);
            int an=0;
            F(i,1,n)ans[i]=1ll*ans[i]*inv2%mod,an+=1ll*ksm(i,k)*ans[i]%mod,an-=an>=mod?mod:0;
            pi(1ll*an*ksm(1ll*n*(n-1)%mod*inv2%mod,mod-2)%mod);return 0;
        }
        topo();if(fl)rt[1]=1;
        F(i,1,n)if(rt[i])vis[i]=1;
        F(i,1,id[0]){
            int v=id[i];
            vis[v]=0;getdis(v,0,0,f[i]);
            mx[Rt=0]=siz[v],size=siz[v];
            findrt(v,0);
            solve(Rt);
        }
        F(i,1,n)ans[i]=1ll*ans[i]*inv2%mod;
        if(!fl){
            int d=id[0]>>1;
            solve1(1,d),solve1(d+1,id[0]);
            solve2(1,d,d+1,d<<1,1),solve2(d+2,id[0],1,id[0]-d-1,1);
        }
        int an=0;
        F(i,1,n)an+=1ll*ans[i]*ksm(i,k)%mod,an-=an>=mod?mod:0;
        pi(1ll*an*ksm(1ll*n*(n-1)%mod*inv2%mod,mod-2)%mod);
        return 0;
    }
}
signed main(){return EMT::main();}

序列

正难则反,考虑所有方案中a的总出现次数(\((n-m+1)k^{n-m}\))减去不合法情况。

  1. a是colorful的。
    显然没有不合法情况。
  2. a中元素互不相同。
    我们设\(f_{i,j}\)表示到i的极长互不相同的后缀串长度为j的方案数,\(g_{i,j}\)表示a出现的次数。
    然后枚举下一位选什么,有:

\[f_{i,j}=f_{i-1,j'}\times\left\{\begin{matrix} k-j' &j'+1==j\\ 0 &j'+1<j\\ 1 &j'+1>j \end{matrix}\right.\]

g的转移和f大致相同,只是在\(j>=m\)时有\(g_{i,j}+=f_{i,j}\)这一项。
于是可以后缀和优化转移。
3. a中元素不一定互不相同。
还是一样的转移,但这次我们让f,g分别为前后缀的方案数,答案呈卷积形式,即:

\[(\sum\limits_{j=0}^{k-1}f_{i,j})\times (\sum\limits_{j=0}^{k-1}f_{n-i-m,j}) \]

然后就没了。

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("sequence.in","r",stdin);freopen("sequence.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=1e5+10,mod=1e9+7;
    int n,k,m,a[N],f[N][500],g[N][500];bool vis[N];
    inline int ksm(int a,int b){
        int ans=1;
        while(b){
            if(b&1)ans=1ll*a*ans%mod;
            a=1ll*a*a%mod;b>>=1;
        }return ans;
    }
    inline bool judge(){
        static int cnt[N];
        int c=0;
        F(i,1,k-1){
            cnt[a[i]]++;
            if(cnt[a[i]]==1)c++;
        }
        F(i,k,m){
            cnt[a[i]]++;
            if(cnt[a[i]]==1)c++;
            if(c==k)return 1;
            cnt[a[i-k+1]]--;
            if(!cnt[a[i-k+1]])c--;
        }return 0;
    }
    inline void DP(int (*f)[500]){
        F(i,1,n){
            F(j,1,k-1)f[i][j]=(1ll*((f[i-1][j-1]-f[i-1][j]+mod)%mod)*(k-j+1)%mod+f[i-1][j])%mod;
            D(j,k-1,0)f[i][j]+=f[i][j+1],f[i][j]-=f[i][j]>=mod?mod:0;
        }
    }
    inline short main(){
        file();
        n=read(),k=read(),m=read();
        int base=1ll*(n-m+1)*ksm(k,n-m)%mod;
        F(i,1,m)a[i]=read();
        int l=m,r=1;
        F(i,1,m){
            if(vis[a[i]]){l=i-1;break;}
            vis[a[i]]=1;
        }memset(vis,0,sizeof(bool)*(k+1));
        D(i,m,1){
            if(vis[a[i]]){r=m-i;break;}
            vis[a[i]]=1;
        }
        if(judge()){pi(base);return 0;}
        if(l==m){
            f[0][0]=1;
            F(i,1,n){
                F(j,1,k-1){
                    f[i][j]=(1ll*((f[i-1][j-1]-f[i-1][j]+mod)%mod)*(k-j+1)%mod+f[i-1][j])%mod;
                    g[i][j]=(1ll*((g[i-1][j-1]-g[i-1][j]+mod)%mod)*(k-j+1)%mod+g[i-1][j])%mod;
                    if(j>=m)g[i][j]+=f[i][j],g[i][j]-=g[i][j]>=mod?mod:0;
                }
                D(j,k-1,0){
                    f[i][j]+=f[i][j+1],f[i][j]-=f[i][j]>=mod?mod:0,
                    g[i][j]+=g[i][j+1],g[i][j]-=g[i][j]>=mod?mod:0;
                }
            }int ans=g[n][0];
            D(i,k,k-m+1)ans=1ll*ans*ksm(i,mod-2)%mod;
            ans=(base-ans+mod)%mod;
            pi(ans);
        }else{
            F(i,0,l)f[0][i]=1;
            F(i,0,r)g[0][i]=1;
            DP(f);DP(g);
            int ans=0;
            F(i,0,n-m)ans+=1ll*f[i][0]*g[n-i-m][0]%mod,ans-=ans>=mod?mod:0;
            ans=(base-ans+mod)%mod;
            pi(ans);
        }
        return 0;
    }
}
signed main(){return EMT::main();}

有向图

考场上少判了个条件。。。然后寄了。
题目就是问哪些点满足以它为根形成一颗只有返祖边的生成树。
然后我们先rand出来一个满足条件的,然后去dfs子树,发现非根节点满足条件是子树中返祖边有且只有一条指向比自己浅的祖先,且这个祖先是满足条件的,于是可以multiset合并一下即可。

Code



#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<set>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("graph.in","r",stdin);freopen("graph.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=1e5+10;
    int head[N],co,n,m,deep[N],rfl[N];bool ban[N],vis[N],vis2[N];std::multiset<int>s[N];
    inline int random(int x){return 1ll*rand()*rand()%x;}
    struct node{int next,to;}e[N*10];
    inline void add(int next,int to){e[++co]={head[next],to},head[next]=co;}
    struct Bit{
        int t[N];
        inline void add(int x,int v){x++;while(x<=n+1)t[x]+=v,x+=x&-x;}
        inline void clear(){memset(t,0,sizeof(int)*(n+2));}
        inline int ask(int x){int ans=0;x++;while(x)ans+=t[x],x-=x&-x;return ans;}
    }bit;
    inline void clear(){
        memset(head,0,sizeof(int)*(n+1)),co=0;
        memset(ban,0,sizeof(bool)*(n+1));
        F(i,1,n)s[i].clear();
    }
    inline bool dfs1(int x){
        vis[x]=vis2[x]=1;bool fl=1;
        for(int i=head[x],j;i;i=e[i].next){
            j=e[i].to;
            if(vis[j])continue;
            if(vis2[j]&&!vis[j]){vis[x]=0;return 0;}
            fl&=dfs1(j);if(!fl){vis[x]=0;return 0;}
        }vis[x]=0;return 1;
    }
    inline void dfs2(int x){
        vis[x]=1;
        for(int i=head[x];i;i=e[i].next){
            int j=e[i].to;
            if(vis[j])s[x].insert(deep[j]);
            else{
                deep[j]=deep[x]+1;dfs2(j);
                std::multiset<int>ss=s[j];
                if(s[x].size()<ss.size())s[x].swap(ss);
                for(auto v:ss)s[x].insert(v);
            }
        }vis[x]=0;
    }
    inline void dfs3(int x){
        vis[x]=1;
        rfl[deep[x]]=x;
        if(deep[x]){
            auto it=s[x].lower_bound(deep[x]);
            if(it==s[x].begin())ban[x]=1;
            else{
                --it;
                if(it!=s[x].begin())ban[x]=1;
                else if(ban[rfl[*it]])ban[x]=1;
            }
        }
        for(int i=head[x],j;i;i=e[i].next)
            if(!vis[j=e[i].to])dfs3(j);
        vis[x]=0;
    }
    inline bool check(int x){
        memset(vis2,0,sizeof(bool)*(n+1));
        if(!dfs1(x)){
            ban[x]=1;
            return 0;
        }deep[x]=0;
        dfs2(x);dfs3(x);
        return 1;
    }
    inline short main(){
        file();
        srand((unsigned long long)(new int));
        int T=read();
        while(T--){
            clear();
            n=read(),m=read();
            F(i,1,m){
                int x=read(),y=read();
                add(x,y);
            }
            int tim=50;bool fl=0;
            while(tim--){
                int x=random(n)+1;
                if(check(x)){fl=1;break;}
            }
            if(!fl){puts("-1");continue;}
            int cnt=0;
            F(i,1,n)if(!ban[i])cnt++;
            if(cnt<0.2*n){puts("-1");continue;}
            F(i,1,n)if(!ban[i])pi(i);pn();
        }
        return 0;
    }
}
signed main(){return EMT::main();}

图形

\(c_i\)表示第i个向量用了几次。首先题目中肯定满足\(\sum\limits_{x_i<0}-c_ix_i=\sum\limits_{x_i>0}c_ix_i\),y也一样,而且这个凸包达到的最高位置一定是\(\sum\limits_{x_i>0}c_ix_i\),因为凸包肯定是先上升到最高位置然后再不断下降,高度和长度增减是连续的。
于是看n很小,m很大,考虑数位dp,设\(dp_{p,a,b,c,d,e,f}\)表示现在最低位是p,\(\sum\limits_{x_i>0}c_ix_i\)让这里进位到了\(a\)状态,\(\sum\limits_{x_i<0}-c_ix_i\)让这里进位到了\(b\)状态,然后c,d就是y的,定义一致。\(e,f\)就是贴不贴上界。
转移时考虑枚举用了哪些边对这一位造成贡献,然后判断合法的话就加上即可。

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("shape.in","r",stdin);freopen("shape.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int mod=998244353;
    int x[10],y[10],dp[32][32][32][32][32][2][2],n,m;
    inline int check(int x,int y,int z){
        if(x^y)return (y<x)?0:1;
        return z;
    }
    inline int dfs(int p,int a,int b,int c,int d,int e,int f){
        if((1<<p)>m)return (!a&&!b&&!c&&!d&&!e&&!f);
        if(~dp[p][a][b][c][d][e][f])return dp[p][a][b][c][d][e][f];
        dp[p][a][b][c][d][e][f]=0;int dm=m>>p&1;
        for(int i=0;i<(1<<n);i++){
            int ta=a,tb=b,tc=c,td=d;
            F(j,1,n)if(i&(1<<(j-1))){
                if(x[j]>0)ta+=x[j];else tb-=x[j];
                if(y[j]>0)tc+=y[j];else td-=y[j];
            }int dta=ta&1,dtb=tb&1,dtc=tc&1,dtd=td&1;
            if(dta==dtb&&dtc==dtd)
            (dp[p][a][b][c][d][e][f]+=dfs(p+1,ta>>1,tb>>1,tc>>1,td>>1,check(dm,dta,e),check(dm,dtc,f)))%=mod;
        }return dp[p][a][b][c][d][e][f];
    }
    inline short main(){
        file();
        n=read(),m=read();
        F(i,1,n)x[i]=read(),y[i]=read();
        memset(dp,-1,sizeof(dp));
        pi((dfs(0,0,0,0,0,0,0)-1+mod)%mod);
        return 0;
    }
}
signed main(){return EMT::main();}

人脑图灵机

把题目转化成方程\(ax+by=m\),然后发现\(ax<ab\),因为如果大于了可以直接换成b然后可以分配的东西更多。
然后先尽可能的买b,买完之后看剩下的数\(p\).
如果\(p<=0.5b\)并且可以分配的个数\(>=1\)或者p任意并且可以分配的个数\(>=2\)就能直接凑出来,否则就无法使用\([0,0.5b]\)分配,只能退还一部分b换成a,那么方程进一步转化成\(a(x+1)-bx=m\%b\)
然后解出来x之后判一下是否合法即可。

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    #define int __int128
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("turing.in","r",stdin);freopen("turing.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    ll n,m,a,b;
    inline short main(){
        file();
        int T=read();
        while(T--){
            n=read(),m=read(),a=read(),b=read();
            if(b*n<m){puts("No");continue;}
            int rest=n-m/b,use=m/b;m%=b;
            if(!m){puts("Yes");continue;}
            if(rest>=2){puts("Yes");continue;}
            else{
                if(m<=b/2){puts("Yes");continue;}
                int x=(m-a)/(a-b);
                if(x*(a-b)!=m-a){puts("No");continue;}
                if(x>use){puts("No");continue;}
                puts("Yes");
            }
        }
        return 0;
    }
}
signed main(){return EMT::main();}

超脑星球

我们把\(i,i+1\)种较大的那个叫做\(mx\),另一个叫做\(mn\)
发现插入一个点时有4种情况:

  1. 插到\(mn\)左边
  2. 插到\(mx\)右边
  3. 插到一个已经用过的\(mx\)右边并且把原来\(mn\)左边的替掉重新找一个
  4. 插到一个已经用过的\(mn\)左边并且把原来\(mx\)右边的替掉重新找一个

发现对于每一种情况的贡献都能分别用一个堆维护,于是维护即可。

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("brain.in","r",stdin);freopen("brain.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
    const int N=1e5+10;
    struct dp{int val,id;friend bool operator <(dp a,dp b){return a.val<b.val;}friend bool operator >(dp a,dp b){return a.val>b.val;}};
    std::priority_queue<dp>q1;std::priority_queue<int>q2;
    //mn mn+mx(of right)
    std::priority_queue<dp,std::vector<dp>,std::greater<dp> >q3;
    std::priority_queue<int,std::vector<int>,std::greater<int> >q4;
    //mx mn+mx(of left)
    int n,m,a[N],b[N];bool vis[N];
    inline int abs(int x){return x<0?-x:x;}
    inline short main(){
        file();
        n=read(),m=read();
        ll ans=0;
        F(i,1,n)a[i]=read();F(i,1,m)b[i]=read();
        F(i,1,n-1){
            q1.push({min(a[i],a[i+1]),i});
            q3.push({max(a[i],a[i+1]),i});
            ans+=abs(a[i+1]-a[i]);
        }std::sort(b+1,b+m+1);
        int l=1,r=m;
        while(l<=r){
            while(vis[q1.top().id])q1.pop();
            while(vis[q3.top().id])q3.pop();
            int v1,v2,v3=-0x3f3f3f3f,v4=-0x3f3f3f3f;
            int x1=b[l],x2=b[r];
            v1=2*(q1.top().val-x1);
            v2=2*(x2-q3.top().val);
            if(q4.size())v3=2*x2-2*q4.top()+2*q1.top().val;
            if(q2.size())v4=-2*x1+2*q2.top()-2*q3.top().val;
            int v=max(max(v1,v2),max(v3,v4));
            if(v<0){F(i,l,r)pi(ans);return 0;}
            ans+=v;
            if(v1==v)q4.push(a[q1.top().id]+a[q1.top().id+1]),vis[q1.top().id]=1,q1.pop(),l++;
            else if(v2==v)q2.push(a[q3.top().id]+a[q3.top().id+1]),vis[q3.top().id]=1,q3.pop(),r--;
            else if(v3==v)q2.push(q4.top()),q4.pop(),q4.push(a[q1.top().id]+a[q1.top().id+1]),vis[q1.top().id]=1,q1.pop(),r--;
            else q4.push(q2.top()),q2.pop(),q2.push(a[q3.top().id]+a[q3.top().id+1]),vis[q3.top().id]=1,q3.pop(),l++;
            pi(ans);
        }
        return 0;
    }
}
signed main(){return EMT::main();}

暗星人

一开始想的是树套树但是空间炸裂了于是换成分块了。。。
但是思路都一样,都是外层维护时间,内层维护区间,然后分块单点修改区间查询套线段树区间修改单点查询。
有个小优化就是如果询问时当前节点没有左右儿子可以直接返回当前懒标记,加上就过了。

Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;typedef unsigned int uint;
    #define pf printf
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("dark.in","r",stdin);freopen("dark.out","w",stdout);}
    inline uint max(uint a,uint b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(uint x){pf("%u ",x);}inline void pn(){pf("\n");}
    const int N=3e5+10;const uint mod=2677114440;
    int rt[N<<2],n,m,tp;uint lans;
    inline uint decode(uint x){return !tp?x:x^lans;}
    struct dp{uint mul,add,val;};
    struct seg{
        int ls[N*300],rs[N*300],tot;uint lzmul[N*300],lzadd[N*300],lzmax[N*300];
        inline void Mul(int p,uint v){
            lzmul[p]=1ll*lzmul[p]*v%mod;
            lzadd[p]=1ll*lzadd[p]*v%mod;
        }
        inline void Add(int p,uint v){lzadd[p]=(0ll+lzadd[p]+v)%mod;}
        inline void Max(int p,uint v){lzmax[p]=max(lzmax[p],v);}
        inline void down(int p){
            if(lzmul[p]!=1){
                if(!ls[p])ls[p]=++tot,lzmul[ls[p]]=1;
                if(!rs[p])rs[p]=++tot,lzmul[rs[p]]=1;
                Mul(ls[p],lzmul[p]),Mul(rs[p],lzmul[p]);
                lzmul[p]=1;
            }
            if(lzadd[p]){
                if(!ls[p])ls[p]=++tot,lzmul[ls[p]]=1;
                if(!rs[p])rs[p]=++tot,lzmul[rs[p]]=1;
                Add(ls[p],lzadd[p]),Add(rs[p],lzadd[p]);
                lzadd[p]=0;
            }
            if(lzmax[p]){
                if(!ls[p])ls[p]=++tot,lzmul[ls[p]]=1;
                if(!rs[p])rs[p]=++tot,lzmul[rs[p]]=1;
                Max(ls[p],lzmax[p]),Max(rs[p],lzmax[p]);
            }
        }
        inline void change(int &p,int l,int r,int ql,int qr,int mul,int add){
            if(!p)p=++tot,lzmul[p]=1;
            if(l>=ql&&r<=qr){
                Mul(p,mul),Add(p,add),Max(p,add);
                return;
            }int mid=(l+r)>>1;down(p);
            if(qr<=mid)change(ls[p],l,mid,ql,qr,mul,add);
            else if(ql>mid)change(rs[p],mid+1,r,ql,qr,mul,add);
            else change(ls[p],l,mid,ql,mid,mul,add),change(rs[p],mid+1,r,mid+1,qr,mul,add);
        }
        inline dp ask(int p,int l,int r,int x){
            if(!p)return{1,0,0};
            if(l==r)return {lzmul[p],lzadd[p],lzmax[p]};
            int mid=(l+r)>>1;if(!ls[p]&&!rs[p])return {lzmul[p],lzadd[p],lzmax[p]};
            down(p);
            if(x<=mid)return ask(ls[p],l,mid,x);
            else return ask(rs[p],mid+1,r,x);
        }
    }segm;
    inline void change(int p,int l,int r,int x,int ql,int qr,int mul,int add){
        // pi(p);pi(l);pi(r);pi(x);pi(ql);pi(qr);pi(mul);pi(add);pn();
        segm.change(rt[p],1,n,ql,qr,mul,add);
        if(l==r)return;
        int mid=(l+r)>>1;
        if(x<=mid)change(p<<1,l,mid,x,ql,qr,mul,add);
        else change(p<<1|1,mid+1,r,x,ql,qr,mul,add);
    }
    inline dp ask(int p,int l,int r,int ql,int qr,int x){
        // pi(p);pi(l);pi(r);pi(ql);pi(qr);pi(x);pn();
        if(l>=ql&&r<=qr)return segm.ask(rt[p],1,n,x);
        int mid=(l+r)>>1;
        if(qr<=mid)return ask(p<<1,l,mid,ql,qr,x);
        if(ql>mid)return ask(p<<1|1,mid+1,r,ql,qr,x);
        dp X=ask(p<<1,l,mid,ql,mid,x),Y=ask(p<<1|1,mid+1,r,mid+1,qr,x);
        // pi(X.mul),pi(X.add),pi(X.val),pn(),
        // pi(Y.mul),pi(Y.add),pi(Y.val),pn();
        return {1ll*Y.mul*X.mul%mod,(Y.add+1ll*Y.mul*X.add)%mod,max(X.val,Y.val)};
    }
    inline short main(){
        file();
        n=read(),m=read(),tp=read();
        F(i,1,m){
            int L=decode(read()),R=decode(read()),a=decode(read()),b=decode(read()),l=decode(read()),r=decode(read()),X=decode(read()),x=decode(read());
            change(1,1,m,i,L,R,a,b);dp v=ask(1,1,m,l,r,X);
            // pi(v.mul),pi(v.add),pi(v.val),pn();
            pi(lans=(1ll*v.mul*x+v.add)%mod^v.val),pn();
        }
        return 0;
    }
}
signed main(){return EMT::main();}

构造题

先tarjan缩好点双然后对于每个点双求解。
构造方案是提出一条从S到T的链,S和T分别是链接两个点双的割点。
拓扑排序出每个点最浅的返祖边,然后从最浅的祖先和父亲向自己练删除的边,
然后从S往T跳,用之前建的删除的边dfs构造方案即可。

Code
#include<vector>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cassert>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("construct.in","r",stdin);freopen("construct.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=5e5+10;bool is[N];
    int head[N],co=1,n,m,fa[N],val[N],dfn[N],low[N],ti,gccnt,mnid,ans[N],mn;struct node{int next,to;}e[N<<1];
    inline void add(int next,int to){e[++co]={head[next],to},head[next]=co;}
    std::vector<int>gcc[N],back[N],link[N];bool use[N];
    inline void dfs(int x){
        if(!use[x])ans[++ans[0]]=x;use[x]=1;
        for(auto y:link[x])if(!use[y])dfs(y);
    }
    inline void solve(std::vector<int>&v){
        int t=v[0],s=0;
        for(int i=1;i<v.size();i++)if(use[v[i]])s=v[i];
        if(!s)for(int i=1;i<v.size();i++)if(is[v[i]])s=v[i];
        if(!s)s=mnid;
        static int q[N],in[N],hd,tl;hd=1,tl=0;
        for(auto x:v)in[x]=0,back[x].clear(),link[x].clear();
        for(int i=1;i<v.size();i++)for(int j=head[v[i]];j;j=e[j].next)
            if(dfn[e[j].to]<dfn[v[i]])back[v[i]].push_back(e[j].to);
        for(int i=1;i<v.size();i++)in[fa[v[i]]]++;
        for(int i=1;i<v.size();i++)if(!in[v[i]])q[++tl]=v[i];
        while(hd<=tl){
            int x=q[hd++];
            if(x==s)continue;
            in[fa[x]]--;
            std::sort(back[x].begin(),back[x].end(),[](int i,int j){return dfn[i]>dfn[j];});
            back[fa[x]].push_back(back[x].back());
            link[back[x].back()].push_back(x);
            link[fa[x]].push_back(x);
            if(!in[fa[x]])q[++tl]=fa[x];
        }
        for(int i=s;i!=t;i=fa[i])
            dfs(i);
    }int s[N],top;
    inline void dfs(int k,int f,int tp){
        dfn[k]=low[k]=++ti;int son=0;fa[k]=f;
        for(int i=head[k],j;i;i=e[i].next)
            if(!dfn[j=e[i].to]){
                s[++top]=i,dfs(j,k,tp);son++;
                low[k]=min(low[k],low[j]);
                if(low[j]>=dfn[k]){
                    if(!tp){
                        is[k]=1;gccnt++;int y;
                        do{y=s[top--];gcc[gccnt].push_back(y);}while(y!=i);
                    }else{
                        std::vector<int>v;v.push_back(k);int y;
                        do{y=s[top--];v.push_back(e[y].to);}while(y!=i);
                        solve(v);
                    }
                }
            }else low[k]=min(low[k],dfn[j]);
        if(!tp&&!f&&son==1)is[k]=0;
    }
    inline short main(){
        file();
        n=read(),m=read();
        if(n==1){puts("1\n1");return 0;}
        F(i,1,n)val[i]=read();
        F(i,1,m){
            int x=read(),y=read();
            add(x,y),add(y,x);
        }dfs(1,0,0);
        int rt=0;
        F(i,1,gccnt){
            static int p[N];
            p[p[0]=1]=e[gcc[i].back()^1].to;
            for(auto x:gcc[i])p[++p[0]]=e[x].to;
            int cnt=0,mnw=1<<30,id=0;
            F(j,1,p[0])if(is[p[j]])cnt++;
                else if(val[p[j]]<mnw)mnw=val[p[j]],id=p[j];
            if(cnt>1)continue;
            if(mnw>mn){
                if(mnid)ans[++ans[0]]=mnid,use[mnid]=1;
                mn=mnw,mnid=id;
                F(j,1,p[0])if(!is[p[j]]&&(gccnt>1||p[j]!=mnid))rt=p[j];
            }else ans[++ans[0]]=id,use[id]=1;
        }pi(max(1,ans[0]));pn();
        memset(dfn,0,sizeof(int)*(n+1));memset(low,0,sizeof(int)*(n+1));
        ti=top=0;dfs(rt,0,1);ans[n]=rt;F(i,1,n)pi(ans[i]);
        return 0;
    }
}
signed main(){return EMT::main();}

点点的圈圈

我们建立权值线段树按照r从小到大排序后以此插入线段树,在线段树中维护一个set找出每个点的直接儿子之后连边即可,dfs好说,儿子总和和自己权值取max即可。

Code
#include<vector>
#include<set>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline ll max(ll a,ll b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(ll x){pf("%lld ",x);}inline void pn(){pf("\n");}
    const int N=1e6+10;bool vis[N];
    struct dp{int x,y,r,w,id;friend bool operator <(dp a,dp b){return a.y==b.y?a.id<b.id:a.y<b.y;}}p[N];
    int n,S[N<<2],L[N],R[N],head[N],co;struct node{int next,to;}e[N];
    inline void add(int next,int to){e[++co]={head[next],to},head[next]=co;}
    inline ll dfs(int x){ll sum=0;for(int i=head[x];i;i=e[i].next)sum+=dfs(e[i].to);return max(sum,p[x].w);}
    struct seg{
        std::set<dp>s[N<<2];
        inline void insert(int p,int l,int r,int x,dp &v){
            s[p].insert(v);if(l==r)return;int mid=(l+r)>>1;
            if(x<=mid)insert(p<<1,l,mid,x,v);else insert(p<<1|1,mid+1,r,x,v);
        }
        inline void ask(int p,int l,int r,int ql,int qr,dp &v){
            if(l>=ql&&r<=qr){
                auto it=s[p].lower_bound({0,v.y-v.r,0,0,0});
                std::vector<dp>del;
                while(it!=s[p].end()&&it->y<=v.y+v.r){
                    if(vis[it->id]){it++;continue;}
                    if(1ll*(S[it->x]-S[v.x])*(S[it->x]-S[v.x])+1ll*(it->y-v.y)*(it->y-v.y)<=1ll*v.r*v.r){
                        del.push_back(*it),vis[it->id]=1,add(v.id,it->id),it++;
                    }else it++;
                }for(auto x:del)s[p].erase(x);
                return;
            }int mid=(l+r)>>1;
            if(qr<=mid)ask(p<<1,l,mid,ql,qr,v);
            else if(ql>mid)ask(p<<1|1,mid+1,r,ql,qr,v);
            else ask(p<<1,l,mid,ql,mid,v),ask(p<<1|1,mid+1,r,mid+1,qr,v);
        }
    }segm;
    inline short main(){
        //file();
        n=read();
        F(i,1,n)p[i].x=read(),p[i].y=read(),p[i].r=read(),p[i].w=read(),S[++S[0]]=p[i].x;
        std::sort(p+1,p+n+1,[](dp a,dp b){return a.r<b.r;});
        F(i,1,n)L[i]=S[++S[0]]=p[i].x-p[i].r,R[i]=S[++S[0]]=p[i].x+p[i].r;
        std::sort(S+1,S+S[0]+1);S[0]=std::unique(S+1,S+S[0]+1)-S-1;
        F(i,1,n)p[i].x=std::lower_bound(S+1,S+S[0]+1,p[i].x)-S,L[i]=std::lower_bound(S+1,S+S[0]+1,L[i])-S,R[i]=std::lower_bound(S+1,S+S[0]+1,R[i])-S,p[i].id=i;
        F(i,1,n){
            segm.ask(1,1,S[0],L[i],R[i],p[i]);
            segm.insert(1,1,S[0],p[i].x,p[i]);
        }F(i,1,n)if(!vis[i])add(n+1,i);
        pi(dfs(n+1));
        return 0;
    }
}
signed main(){return EMT::main();}

点点的计算

发现\(T(i,j)=iC(i-1,j-1)\)
然后答案求得就是\(\operatorname{lcm}(n, n-1,...,n-k+1)\)
然后考虑用主席树构造D数组使得:
\(\prod\limits_{i=n-k+1}^nD_{n,i}=\operatorname{lcm}(n, n-1,...,n-k+1)\)
然后对于每个质因子维护一个栈,考虑加入一个n时使得\(D_{n,n}=n\),那么要去除之前质因子的贡献,所以一直弹栈弹到没有贡献了或者自己次数不够了就行了,
然后就是一个普通的单点修改区间求乘机的主席树了。

Code
#include<stack>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
    const int N=1e5+10,mod=1e9+7;
    struct dp{int x,cnt;};std::stack<dp>s[N];
    int n,k,rt[N],prime[N],mv[N],co,inv[N],A,B,Mod,ans,c[N<<1],d[N<<1];bool vis[N];
    inline int ksm(int a,int b){
        int ans=1;
        while(b){
            if(b&1)ans=1ll*a*ans%mod;
            a=1ll*a*a%mod;b>>=1;
        }return ans;
    }
    struct seg{
        int val[N*200],ls[N*200],rs[N*200],tot;
        inline void change(int x,int &y,int l,int r,int X,int v){
            if(!y)y=++tot,ls[y]=ls[x],rs[y]=rs[x],val[y]=val[x];if(!val[y])val[y]=1;
            val[y]=1ll*val[y]*v%mod;if(l==r)return;int mid=(l+r)>>1;
            if(X<=mid){
                if(ls[y]==ls[x])ls[y]=0;
                change(ls[x],ls[y],l,mid,X,v);
            }else{
                if(rs[y]==rs[x])rs[y]=0;
                change(rs[x],rs[y],mid+1,r,X,v);
            }
        }
        inline int ask(int p,int l,int r,int ql,int qr){
            if(!p)return 1;if(l>=ql&&r<=qr)return val[p];
            int mid=(l+r)>>1;
            if(qr<=mid)return ask(ls[p],l,mid,ql,qr);
            if(ql>mid)return ask(rs[p],mid+1,r,ql,qr);
            return 1ll*ask(ls[p],l,mid,ql,mid)*ask(rs[p],mid+1,r,mid+1,qr)%mod;
        }
    }segm;
    inline void init(int n=1e5){
        inv[0]=inv[1]=1;
        F(i,2,n){
            inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
            if(!vis[i])prime[++co]=i,mv[i]=i;
            F(j,1,co){
                if(prime[j]*i>n)break;
                vis[prime[j]*i]=1;
                mv[prime[j]*i]=prime[j];
                if(i%prime[j]==0)break;
            }
        }
    }
    inline void prework(int n=1e5){
        F(i,1,n){
            int now=i;
            while(now!=1){
                int x=mv[now],cnt=0;
                while(now%x==0)now/=x,cnt++;
                int rcnt=cnt;
                while(cnt&&!s[x].empty()){
                    auto v=s[x].top();s[x].pop();
                    if(v.cnt<=cnt)cnt-=v.cnt,segm.change(rt[i-1],rt[i],1,n,v.x,ksm(inv[x],v.cnt));
                    else segm.change(rt[i-1],rt[i],1,n,v.x,ksm(inv[x],cnt)),s[x].push({v.x,v.cnt-cnt}),cnt=0;
                }
                s[x].push({i,rcnt});
            }segm.change(rt[i-1],rt[i],1,n,i,i);
        }
    }
    inline short main(){
        //file();
        init(),prework();
        int Q=read();
        n=read(),k=read();
        pi(ans=segm.ask(rt[n],1,1e5,n-k+1,n));pn();
        A=read(),B=read(),Mod=read();
        F(i,2,Q)c[i]=read();F(i,2,Q)d[i]=read();
        F(i,2,Q){
            n=(1ll*A*ans+c[i])%Mod+1;
            k=(1ll*B*ans+d[i])%n+1;
            pi(ans=segm.ask(rt[n],1,1e5,n-k+1,n));pn();
        }
        return 0;
    }
}
signed main(){return EMT::main();}

点点的最大流

由于每个点至多属于一个环,所以可以考虑断环为链,由于每个环中权值最小的那个边一定先于其他边满流,所以其实可以断开这个边,把其他的边边权加上自己的边权,这样的话如果只考虑查询操作的话就是一个链加,支持查询链上最小值然后附带着最小值的id,答案就是S到T的最小值,普通的LCT板子。
然后考虑修改操作,可以分情况考虑 (虽然用一个set可以避免但是我这种小常数选手怎么会屈服于此呢)

  1. 修改的边是原来环上断掉的那条边并且修改后仍然是最小边
    直接链加上修改前后的差值即可。
  2. 修改的边是原来环上断掉的那条边并且修改后不是最小边了
    我们可以链查出那个LCT上将要成为最小边的边,然后断掉它的两端,连上原来最小边的两端,然后链加上前后差值即可,注意新连的那条边的权值设成什么,是个小细节
  3. 修改的边不是原来环上断掉的那条边并且修改后仍然不是最小边
    直接单点修改即可。
  4. 修改的边不是原来环上断掉的那条边并且修改后变成了最小边
    和2情况类似,还是断掉这条边连上原来那个断边然后链加个差值即可。

然后就没了,下次还不会求边双

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define D(i,a,b) for(int i=a;i>=b;i--)
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%d",x);}inline void pn(){pf("\n");}
    const int N=1e6+10;
    int bel[N],n,m,head[N],co=1;
    struct node{int next,to,w;}e[N];
    inline void add(int next,int to,int w){e[++co]={head[next],to,w},head[next]=co;}
    namespace LCT{
        int val[N],lz1[N],fa[N],mn[N],son[N][2],mnid[N];bool lz2[N];
        inline bool nroot(int x){return son[fa[x]][1]==x||son[fa[x]][0]==x;}
        inline void up(int x){
            mn[x]=val[x],mnid[x]=x;
            if(son[x][0]&&mn[x]>mn[son[x][0]])mn[x]=mn[son[x][0]],mnid[x]=mnid[son[x][0]];
            if(son[x][1]&&mn[x]>mn[son[x][1]])mn[x]=mn[son[x][1]],mnid[x]=mnid[son[x][1]];
        }
        inline void rev(int x){std::swap(son[x][1],son[x][0]),lz2[x]^=1;}
        inline void add(int x,int v){lz1[x]+=v,val[x]+=v,mn[x]+=v;}
        inline void down(int x){
            if(lz1[x]){if(son[x][0])add(son[x][0],lz1[x]);if(son[x][1])add(son[x][1],lz1[x]);lz1[x]=0;}
            if(lz2[x])rev(son[x][0]),rev(son[x][1]),lz2[x]=0;
        }
        inline void res(int x){
            int y=fa[x],z=fa[y],k=(son[y][1]==x),w=son[x][!k];
            son[x][!k]=y;if(nroot(y))son[z][son[z][1]==y]=x;
            son[y][k]=w,fa[x]=z,fa[y]=x;if(w)fa[w]=y;up(y);
        }
        inline void splay(int x){
            static int s[N];int y=x,z=0;s[++z]=x;
            while(nroot(y))s[++z]=(y=fa[y]);
            while(z)down(s[z--]);
            while(nroot(x)){
                y=fa[x],z=fa[y];
                if(nroot(y))res((son[z][1]==y)^(son[y][1]==x)?x:y);
                res(x);
            }up(x);
        }
        inline void access(int x){for(int y=0;x;x=fa[y=x])splay(x),son[x][1]=y,up(x);}
        inline void makeroot(int x){access(x),splay(x),rev(x);}
        inline void split(int x,int y){makeroot(x),access(y),splay(y);}
        inline void link(int x,int y){makeroot(x),fa[x]=y;}
        inline void cut(int x,int y){split(x,y),son[y][0]=fa[x]=0,up(y);}
        inline void change(int x,int v){makeroot(x),val[x]=v,up(x);}
    }
    int q[N];bool vis[N];
    struct dp{
        int val,id;std::vector<int>hv;
        inline void init(){
            int hd,tl;val=1e9;q[hd=tl=1]=hv[0];
            for(auto x:hv)vis[x]=0;
            while(hd<=tl){
                int x=q[hd++];if(vis[x])continue;vis[x]=1;
                for(int i=head[x],j;i;i=e[i].next)if(bel[j=e[i].to]==bel[x]){
                    if(e[i].w<val)val=e[i].w,id=i>>1;
                    q[++tl]=j;
                }
            }
            q[hd=tl=1]=hv[0];for(auto x:hv)vis[x]=0;
            while(hd<=tl){
                int x=q[hd++];if(vis[x])continue;vis[x]=1;
                for(int i=head[x],j;i;i=e[i].next)if(bel[j=e[i].to]==bel[x]&&!vis[j]){
                    if((i>>1)==id)continue;
                    int Id=(i>>1)+n;
                    LCT::val[Id]=LCT::mn[Id]=e[i].w,LCT::mnid[Id]=Id;
                    LCT::link(x,Id),LCT::link(Id,j);
                    q[++tl]=j;
                }
            }if(val!=1e9)LCT::split(e[id<<1].to,e[id<<1|1].to),LCT::add(e[id<<1|1].to,val);
        }
        inline void change(int x,int v){
            if((x>>1)==id){
                LCT::split(e[id<<1].to,e[id<<1|1].to);
                int ano=LCT::mn[e[id<<1|1].to]-val;
                if(ano>=v)LCT::add(e[id<<1|1].to,v-val),val=v;
                else{
                    int mid=LCT::mnid[e[id<<1|1].to],it=mid-n;
                    LCT::val[id+n]=LCT::mn[id+n]=val+v,LCT::mnid[id+n]=id+n;
                    LCT::cut(e[it<<1].to,mid),LCT::cut(e[it<<1|1].to,mid);
                    LCT::link(e[id<<1].to,id+n),LCT::link(e[id<<1|1].to,id+n);
                    LCT::split(e[it<<1].to,e[it<<1|1].to),LCT::add(e[it<<1|1].to,ano-val);
                    val=ano,id=it;
                }
            }else{
                if(v>=val)LCT::change((x>>1)+n,v+val);
                else{
                    int it=(x>>1),mid=it+n;
                    LCT::val[id+n]=LCT::mn[id+n]=val<<1,LCT::mnid[id+n]=id+n;
                    LCT::cut(e[it<<1].to,mid),LCT::cut(e[it<<1|1].to,mid);
                    LCT::link(e[id<<1].to,id+n),LCT::link(e[id<<1|1].to,id+n);
                    LCT::split(e[it<<1].to,e[it<<1|1].to),LCT::add(e[it<<1|1].to,v-val);
                    val=v,id=it;
                }
            }
        }
    }p[N];
    int gccnt,dfn[N],low[N],ti;bool bridge[N];
    inline void tj(int x,int ine){
        dfn[x]=low[x]=++ti;
        for(int i=head[x],j;i;i=e[i].next)if(!dfn[j=e[i].to]){
            tj(j,i),low[x]=min(low[x],low[j]);
            if(low[j]>dfn[x])bridge[i]=bridge[i^1]=1;
        }else if(i!=(ine^1))low[x]=min(low[x],dfn[j]);
    }
    inline void Dfs(int x){
        bel[x]=gccnt;p[gccnt].hv.push_back(x);
        for(int i=head[x],j;i;i=e[i].next)if(!bel[j=e[i].to]&&!bridge[i])Dfs(j);
    }
    inline short main(){
        n=read(),m=read();
        F(i,1,m){
            int x=read(),y=read(),z=read();
            add(x,y,z),add(y,x,z);
        }tj(1,0);F(i,1,n)if(!bel[i])gccnt++,Dfs(i);
        F(i,1,n)LCT::val[i]=1e9;F(i,1,gccnt)p[i].init();
        for(int i=2;i<=co;i+=2)if(bel[e[i].to]!=bel[e[i^1].to]){
            int id=(i>>1)+n;
            LCT::val[id]=LCT::mn[id]=e[i].w,LCT::mnid[id]=id;
            LCT::link(e[i].to,id),LCT::link(id,e[i^1].to);
        }
        int Q=read();
        while(Q--){
            int opt=read();
            if(!opt){
                int x=read(),y=read();
                LCT::split(x,y),pi(LCT::mn[y]),pn();
            }else{
                int x=read()<<1,val=read();
                if(bel[e[x].to]==bel[e[x^1].to])p[bel[e[x].to]].change(x,val);
                else LCT::change((x>>1)+n,val);
            }
        }
        return 0;
    }
}
signed main(){return EMT::main();}

我醉

考虑用哈希判断回文,然后用点分治进行匹配。
匹配肯定是一个长链一个短链,首先二分答案,这样就能算出来一条链是不是长链,另一条链的长度。
如果是长链的话,判断一下和短链匹配之后剩下那一部分是否回文,如果回文就加入哈系表中等待匹配。
如果是短链的话,查询哈系表中是否有和自己相等的哈希值,如果有的话,就满足条件。
但是这样需要正反两遍点分治。可以短链也加到哈系表中,然后长链判断剩下那部分回文之后在哈系表中查一下自己的哈希值有没有即可。

Code


#include<set>
#include<cstring>
#include<iostream>
#include<vector>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;typedef unsigned long long ull;
	#define pf printf
	#define pb push_back
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("name.in","r",stdin);freopen("name.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=1e5+10,mod=114514;
	std::vector<ull>hv1,hv2;
	struct hash_table{
		int head[mod],co,s[mod],top;
		struct node{ull key;int next;}e[N];
		inline int f(ull key){return key%mod;}
		inline bool find(ull key){for(int i=head[f(key)];i;i=e[i].next)if(e[i].key==key)return 1;return 0;}
		inline void add(ull key){e[++co]={key,head[f(key)]},head[f(key)]=co;s[++top]=f(key);}
		inline void clear(){while(top)head[s[top--]]=0;co=0;}
	}hs1,hs2;
	int n,head[N],co,deep[N];
	struct node{int next,to,w;}e[N<<1];
	inline void add(int next,int to,int w){e[++co]={head[next],to,w},head[next]=co;}
	ull p[N];
	int siz[N],mx[N],rt,maxn,nowlen,ans=1;bool vis[N],fl;
	inline void findrt(int x,int fa){
		siz[x]=1;mx[x]=0;
		for(int i=head[x],j;i;i=e[i].next)if((j=e[i].to)!=fa&&!vis[j]){
			findrt(j,x),siz[x]+=siz[j],mx[x]=max(mx[x],siz[j]);
		}mx[x]=max(mx[x],maxn-siz[x]);if(mx[x]<mx[rt])rt=x;
	}
	ull hash1[N],hash2[N];
	inline void getdis(int x,int dis,int fa){
		if(fl)return;
		int ano=nowlen-dis;
		if(hash1[dis]==hash2[dis])ans=max(ans,dis);
		if(ano<=dis){
			ano=dis-ano;
			if(hash1[ano]==hash2[ano]){
				ull val=hash2[dis]-hash2[ano]*p[dis-ano];
				if(hs1.find(val)){fl=1;return;}
				hv2.pb(val);if(dis*2==nowlen)hv1.pb(val);
			}
		}else{
			ull val=hash2[dis];
			if(hs2.find(val)){fl=1;return;}
			hv1.pb(val);
		}
		for(int i=head[x],j;i;i=e[i].next)if(!vis[j=e[i].to]&&j!=fa){
			hash1[dis+1]=p[dis]*e[i].w+hash1[dis],
			hash2[dis+1]=13331*hash2[dis]+e[i].w;
			getdis(j,dis+1,x);
		}
	}
	inline void getsiz(int x,int fa){
		siz[x]=1;
		for(int i=head[x],j;i;i=e[i].next)if((j=e[i].to)!=fa&&!vis[j])getsiz(j,x),siz[x]+=siz[j];
	}
	inline void dfs(int x){
		if(fl)return;
		vis[x]=1;
		hs1.clear(),hs2.clear();
		for(int i=head[x],j;i;i=e[i].next)
			if(!vis[j=e[i].to]){
				hv1.clear(),hv2.clear();
				hash1[1]=hash2[1]=e[i].w;
				getdis(j,1,x);
				if(fl)return;
				for(auto v:hv1)hs1.add(v);
				for(auto v:hv2)hs2.add(v);
			}
		for(int i=head[x],j;i;i=e[i].next)if(!vis[j=e[i].to]){
			getsiz(j,x);
			maxn=siz[j],mx[rt=0]=n+1;
			findrt(j,x),dfs(rt);
		}
	}
	inline bool check(int len){
		memset(vis,0,sizeof(bool)*(n+1));
		fl=0;mx[rt=0]=n+1,maxn=n;nowlen=len;
		findrt(1,0);dfs(rt);
		return fl;
	}
	inline void test(){
		pi(check(8));exit(0);
	}
	inline short main(){
		file();
		n=read();
		F(i,2,n){
			int x=read(),y=read(),z=read();
			add(x,y,z),add(y,x,z);
		}p[0]=1;F(i,1,n)p[i]=p[i-1]*13331;
		if(1){
			int l=1,r=n>>1;
			while(l<=r){
				int mid=(l+r)>>1,len=mid<<1;
				if(check(len))l=mid+1,ans=max(ans,len);
				else r=mid-1;
			}
		}
		if(1){
			int l=1,r=(n-1)>>1;
			while(l<=r){
				int mid=(l+r)>>1,len=mid<<1|1;
				if(check(len))l=mid+1,ans=max(ans,len);
				else r=mid-1;
			}
		}pi(ans);
		return 0;
	}
}
signed main(){return EMT::main();}

梧桐依旧

考虑用burnside定理求解。
首先原始的柿子是

\[|X/G|=\frac{1}{|G|}\sum\limits_{g\in G}|X^g| \]

那么求得其实就是\(|X^g|\)即通过左乘B矩阵变换后不变的矩阵A构成的集合大小。
那么把\(|G|\)挪过去,也就是要求出\(|G|\times |X/G|\)
首先看第一项,也就是符合\(det(B)\not \equiv 0(\bmod k)\)\(B\)的数量。
行列式不为0也就是矩阵满秩。那么矩阵可以拆成n行线性无关的向量组。
第一行可以填除了0之外的任意数,即\(k^n-1\)
第二行要和第一行线性无关,第一行可以组成\(k\)个不同向量,那么有方案\(k^n-k\)
第一行和第二行可以组成\(k^2\)个不同向量,那么第三行方案数就是\(k^n-k^2\)
那么以此类推,\(|G|=\prod\limits_{i=0}^{n-1}k^n-k^i\)
然后考虑后面一项,求出有多少种变换后的等价类。
两个矩阵同构当且仅当一个矩阵的行向量都能由另一个矩阵的行向量组线性表出。
首先秩不同的矩阵不同构,可以分秩考虑,若秩为i,方案数依据上面的方法有\(\prod_{j=0}^{i-1}k^n-k^j\)
左乘一个可逆矩阵相当于做初等行变换,前k行线性无关,还是类似上面的办法求出秩相同的矩阵行向量组能表示出的向量数量是一定的,为\(\prod\limits_{j=0}^{i-1}k^i-k^j\)
那么除掉就好了。
那么最后的柿子就变成了\((\prod\limits_{i=0}^{n-1}k^n-k^i)\sum\limits_{i=1}^{n}\prod\limits_{j=0}^{i-1}\frac{k^n-k^j}{k^i-k^j}\)

Code
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("tree.in","r",stdin);freopen("tree.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int mod=998244353,N=3e7+10;
	inline int ksm(int a,int b){
		int ans=1;
		while(b){
			if(b&1)ans=1ll*a*ans%mod;
			a=1ll*a*a%mod;b>>=1;
		}return ans;
	}
	int pw[N],n,k,f[N],pre[N],invk;
	inline short main(){
		file();
		pw[0]=1;n=read(),k=read(),invk=ksm(k,mod-2);
		F(i,1,n)pw[i]=1ll*pw[i-1]*k%mod;
		pre[0]=pw[n]-1;
		for(int i=1;i<n;i++)pre[i]=1ll*pre[i-1]*(pw[n]-pw[i]+mod)%mod;
		int now=ksm(pre[n-1],mod-2),sum=1ll*now*pre[n-1]%mod;
		D(i,n-1,1){
			now=1ll*now*pw[i+1]%mod*(pw[i]-invk+mod)%mod;
			sum+=1ll*now*pre[i-1]%mod,sum-=sum>=mod?mod:0;
		}sum=1ll*(sum+1)*pre[n-1]%mod;
		pi(sum);
		return 0;
	}
}
signed main(){return EMT::main();}

卿且去

整除可以看成一个偏序关系。
考虑建图,如果\(i|j\)从i向j连一条有向边,那么要求的就是最大反链长度之和。
有dilworth定理可知最小链覆盖等于最大反链长度,那么转为求最小链覆盖。
有最优策略:
每次覆盖当前最小的值x然后覆盖\(2x,4x,8x\)
依据上述策略有贡献的是:

  1. S中的奇数
  2. S中是2的但不是4的倍数并且质因数只有2在S中出现的数
    然后考虑每个数贡献求和。
    考虑x的哪些质因子被选上时有贡献:
  3. \(2\not | x\)贡献为\((2^{p(n)-q(x)})\times (2^{q(x)}-1)\)
  4. \(2|x \&\& 4\not | x\)贡献为\(2^{p(n)-q(x)}\)
    其中\(p(i)\)表示i以内质数个数,\(q(i)\)表示i质因子个数。
    然后发现\(2^{w(x)}\)为积性函数,于是min25筛即可。
Code


#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
	typedef long long ll;typedef double db;
	#define pf printf
	#define F(i,a,b) for(int i=a;i<=b;i++)
	#define D(i,a,b) for(int i=a;i>=b;i--)
	inline ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
	inline void file(){freopen("yyds.in","r",stdin);freopen("yyds.out","w",stdout);}
	inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
	inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
	const int N=1e6+10,mod=998244353,inv2=(mod+1)>>1;
	ll n;int sq;
	inline int ksm(int a,int b){
		int ans=1;
		while(b){
			if(b&1)ans=1ll*a*ans%mod;
			a=1ll*a*a%mod;b>>=1;
		}return ans;
	}
	namespace pai{
		ll w[N];int sp[N],prime[N],id1[N],id2[N],g[N],co;bool vis[N];
		inline void init(){
			F(i,2,sq){
				if(!vis[i])prime[++co]=i,sp[co]=sp[co-1]+1;
				F(j,1,co){
					if(i*prime[j]>sq)break;
					vis[i*prime[j]]=1;
					if(i%prime[j]==0)break;
				}
			}
		}
		inline void solve(){
			for(ll l=1,r;l<=n;l=r+1){
				r=n/(n/l),w[++w[0]]=n/l,g[w[0]]=(w[w[0]]-1)%mod;
				if(n/l<=sq)id1[n/l]=w[0];else id2[r]=w[0];
			}
			F(i,1,co){
				F(j,1,w[0]){
					if(1ll*prime[i]*prime[i]>w[j])break;
					int k=w[j]/prime[i]<=sq?id1[w[j]/prime[i]]:id2[n/(w[j]/prime[i])];
					g[j]-=g[k]-sp[i-1],g[j]+=g[j]<0?mod:0,g[j]-=g[j]>=mod?mod:0;
				}
			}
		}
	}
	namespace min_25{
		ll w[N];int ans,sp[N],prime[N],id1[N],id2[N],g[N],co;bool vis[N];
		inline void init(){
			F(i,2,sq){
				if(!vis[i])prime[++co]=i,sp[co]=sp[co-1]+(i==2?0:inv2),sp[co]-=sp[co]>=mod?mod:0;
				F(j,1,co){
					if(i*prime[j]>sq)break;
					vis[i*prime[j]]=1;
					if(i%prime[j]==0)break;
				}
			}
		}
		inline int S(ll x,int y){
			if(prime[y]>=x)return 0;
			int k=x<=sq?id1[x]:id2[n/x];
			int ans=(g[k]-sp[y]+mod)%mod;
			F(i,y+1,co){
				if(1ll*prime[i]*prime[i]>x)break;
				int e=1;
				for(ll p=prime[i];p<=x;e++,p*=prime[i]){
					int v=prime[i]==2?0:inv2;
					ans+=1ll*v*(S(x/p,i)+(e!=1))%mod;
					ans-=ans>=mod?mod:0;
				}
			}
			return ans;
		}
		inline void solve(){
			for(ll l=1,r;l<=n;l=r+1){
				r=n/(n/l),w[++w[0]]=n/l;
				if(n/l<=sq)id1[n/l]=w[0];else id2[r]=w[0];
			}
			F(i,1,w[0])g[i]=1ll*(pai::g[i]+mod-1)*inv2%mod;
			g[w[0]]=0;
			ans=S(n,0);
		}
	}
	namespace pai2{
		ll w[N];int sp[N],prime[N],id1[N],id2[N],g[N],co;bool vis[N];
		inline void init(){
			F(i,2,sq){
				if(!vis[i])prime[++co]=i,sp[co]=sp[co-1]+1;
				F(j,1,co){
					if(i*prime[j]>sq)break;
					vis[i*prime[j]]=1;
					if(i%prime[j]==0)break;
				}
			}
		}
		inline void solve(){
			for(ll l=1,r;l<=n;l=r+1){
				r=n/(n/l),w[++w[0]]=n/l,g[w[0]]=(w[w[0]]-1)%mod;
				if(n/l<=sq)id1[n/l]=w[0];else id2[r]=w[0];
			}
			F(i,1,co){
				F(j,1,w[0]){
					if(1ll*prime[i]*prime[i]>w[j])break;
					int k=w[j]/prime[i]<=sq?id1[w[j]/prime[i]]:id2[n/(w[j]/prime[i])];
					g[j]-=g[k]-sp[i-1],g[j]+=g[j]<0?mod:0,g[j]-=g[j]>=mod?mod:0;
				}
			}
		}
	}
	namespace min_252{
		ll w[N];int ans,sp[N],prime[N],id1[N],id2[N],g[N],co;bool vis[N];
		inline void init(){
			F(i,2,sq){
				if(!vis[i])prime[++co]=i,sp[co]=sp[co-1]+(i==2?0:inv2),sp[co]-=sp[co]>=mod?mod:0;
				F(j,1,co){
					if(i*prime[j]>sq)break;
					vis[i*prime[j]]=1;
					if(i%prime[j]==0)break;
				}
			}
		}
		inline int S(ll x,int y){
			if(prime[y]>=x)return 0;
			int k=x<=sq?id1[x]:id2[n/x];
			int ans=(g[k]-sp[y]+mod)%mod;
			F(i,y+1,co){
				if(1ll*prime[i]*prime[i]>x)break;
				int e=1;
				for(ll p=prime[i];p<=x;e++,p*=prime[i]){
					int v=prime[i]==2?0:inv2;
					ans+=1ll*v*(S(x/p,i)+(e!=1))%mod;
					ans-=ans>=mod?mod:0;
				}
			}
			return ans;
		}
		inline void solve(){
			for(ll l=1,r;l<=n;l=r+1){
				r=n/(n/l),w[++w[0]]=n/l;
				if(n/l<=sq)id1[n/l]=w[0];else id2[r]=w[0];
			}
			F(i,1,w[0])g[i]=1ll*(pai2::g[i]+mod-1)*inv2%mod;g[w[0]]=0;
			ans=(S(n,0)+1)%mod;
		}
	}
	inline short main(){
		file();
		n=read();sq=sqrt(n);
		pai::init(),pai::solve();
		min_25::init(),min_25::solve();
		int ans=1ll*ksm(2,pai::g[1])*((n-1)/2)%mod;
		ans-=1ll*min_25::ans*ksm(2,pai::g[1])%mod,ans+=ans<0?mod:0;
		n/=2,sq=sqrt(n);
		pai2::init(),pai2::solve();
		min_252::init(),min_252::solve();
		ans+=1ll*min_252::ans*inv2%mod*ksm(2,pai::g[1])%mod,ans-=ans>=mod?mod:0;
		pi(ans);
		return 0;
	}
}
signed main(){return EMT::main();}
posted @ 2021-12-26 20:48  letitdown  阅读(263)  评论(5编辑  收藏  举报