Codechef July Challenge 2020

PTMSSNG

先当于求 x 里出现奇数次的,y 里出现奇数次的,异或和一下就好了

#include<bits/stdc++.h>
#define D(...) fprintf(stderr,__VA_ARGS__)
#define int long long
#define LL long long
#define MP make_pair
#define PB push_back
#define fi first
#define se second
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef pair<int,int>PII;typedef vector<int>VI;typedef vector<PII>VII;
template<typename T>void rd(T&x){int f=0,c;while(!isdigit(c=getchar()))f^=!(c^45);x=(c&15);while(isdigit(c=getchar()))x=x*10+(c&15);if(f)x=-x;}
template<typename T>void pt(T x,int c=-1){if(x<0)putchar('-'),x=-x;if(x>9)pt(x/10);putchar(x%10+48);if(c!=-1)putchar(c);}
int T;
signed main(){
	rd(T);
	while(T--){
		int n;rd(n),n=n*4-1;
		int x=0,y=0;
		rep(i,1,n){
			int k1,k2;rd(k1),rd(k2);
			x^=k1,y^=k2;
		}
		pt(x,' '),pt(y,'\n');
	}
	return 0;
}

CHFNSWPS

把 A 和 B 共有的删掉,那么只要把剩下的最小的一半和最大的一半 swap 即可,注意代价还要对序列里的最小值的两倍取 min,因为可以通过最小值实现两倍代价交换两个数

#include<bits/stdc++.h>
#define D(...) fprintf(stderr,__VA_ARGS__)
#define int long long
#define LL long long
#define MP make_pair
#define PB push_back
#define fi first
#define se second
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef pair<int,int>PII;typedef vector<int>VI;typedef vector<PII>VII;
template<typename T>void rd(T&x){int f=0,c;while(!isdigit(c=getchar()))f^=!(c^45);x=(c&15);while(isdigit(c=getchar()))x=x*10+(c&15);if(f)x=-x;}
template<typename T>void pt(T x,int c=-1){if(x<0)putchar('-'),x=-x;if(x>9)pt(x/10);putchar(x%10+48);if(c!=-1)putchar(c);}
const int N=200005;
int T,n,A[N],B[N],C[N*2];
signed main(){
	rd(T);
	while(T--){
		rd(n);
		rep(i,1,n)rd(A[i]),C[i]=A[i];
		rep(i,1,n)rd(B[i]),C[i+n]=B[i];
		sort(A+1,A+1+n);
		sort(B+1,B+1+n);
		sort(C+1,C+1+n*2);
		int k1=1,k2=1;
		VI num;
		int ans=0;
		for(int i=1;i<=n*2;i+=2){
			if(C[i]!=C[i+1]){
				puts("-1");
				goto qaq;
			}
			while(k1<=n&&A[k1]<C[i])++k1;
			while(k2<=n&&B[k2]<C[i])++k2;
			if(k1>n||A[k1]!=C[i]||k2>n||B[k2]!=C[i]){
				num.PB(C[i]);
			}else{
				++k1,++k2;
			}
		}
		assert(SZ(num)%2==0);
		rep(i,0,SZ(num)/2-1){
			ans+=min(num[i],C[1]*2);
		}
		pt(ans,'\n');
		qaq:;
		rep(i,1,n)A[i]=0,B[i]=0,C[i]=C[i+n]=0;
	}
	return 0;
}

DRCHEF

感觉做法写烦了,暴力的模拟这个过程:每次查找有没有数字在 [(x+1)/2,x] 的,如果有,干最小的满足条件的,否则干最大的一个数

#include<bits/stdc++.h>
#define D(...) fprintf(stderr,__VA_ARGS__)
#define int long long
#define LL long long
#define MP make_pair
#define PB push_back
#define fi first
#define se second
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef pair<int,int>PII;typedef vector<int>VI;typedef vector<PII>VII;
template<typename T>void rd(T&x){int f=0,c;while(!isdigit(c=getchar()))f^=!(c^45);x=(c&15);while(isdigit(c=getchar()))x=x*10+(c&15);if(f)x=-x;}
template<typename T>void pt(T x,int c=-1){if(x<0)putchar('-'),x=-x;if(x>9)pt(x/10);putchar(x%10+48);if(c!=-1)putchar(c);}
const int N=100005,INF=0X3F3F3F3F;
int TT,n,x,a[N],lim[N];
struct cmp{bool operator()(const int&k1,const int&k2)const{return a[k1]^a[k2]?a[k1]<a[k2]:k1>k2;}};
set<int,cmp>S;
set<int>T;
signed main(){
	rd(TT);
	while(TT--){
		S.clear(),T.clear();
		rd(n);rd(x);
		rep(i,1,n)rd(a[i]);
		sort(a+1,a+1+n);
		rep(i,1,n)lim[i]=a[i],S.insert(i);
		sort(a+1,a+1+n);
		int ans=0;
		while(SZ(S)){
			++ans;
			a[n+1]=(x+1)/2;
			auto tmp=S.lower_bound(n+1);
			if(tmp==S.end())--tmp;
			else{
				if(a[*tmp]>x)tmp=--S.end();
			}
			int i=*tmp;
//			printf(">>> %lld %lld:",i,x);
//			rep(i,1,n){
//				printf("%lld ",a[i]);
//			}
//			puts("");
			if(a[i]<=x){
				S.erase(i);
				T.erase(i);
				x=a[i]*2;
				a[i]=0;
			}else{
				S.erase(i);
				a[i]-=x;
				S.insert(i);
				T.insert(i);
				x*=2;
			}
			for(auto it=T.begin();it!=T.end();){
				S.erase(*it);
				a[*it]=min(a[*it]*2,lim[*it]);
				S.insert(*it);
				if(a[*it]<0||a[*it]==lim[*it]){
					T.erase(it++);
				}else{
					++it;
				}
			}
		}
		pt(ans,'\n');
	}
	return 0;
}

DRGNDEN

用线段树正着,反着维护区间从左端点开始到右端点及其右边终止的权值和,update 的时候稍微操作一下,是个经典的套路

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
typedef long long LL;
const int N=200005;
int n,Q,h[N],a[N],mxx;LL res;
struct SEG{
	int pd,mx[N*4];LL sum[N*4];
	void upd1(int k1){
		mx[k1]=max(mx[k1*2],mx[k1*2+1]);
	}
	LL qry(int k1,int k2,int k3,int k4){
		if(mx[k1]<=k4)return 0;
		int al=pd?h[n-k2+1]:h[k2];
		if(al>k4)return sum[k1];
		if(k2==k3)return 0;
		int mid=(k2+k3)>>1;
		if(mx[k1*2]<=k4)return qry(k1*2+1,mid+1,k3,k4);
		else return qry(k1*2,k2,mid,k4)+sum[k1]-sum[k1*2];
	}
	void bud(int k1,int k2,int k3){
		if(k2==k3){
			if(pd){
				mx[k1]=h[n-k2+1];
				sum[k1]=a[n-k2+1];
			}else{
				mx[k1]=h[k2];
				sum[k1]=a[k2];
			}
			return;
		}
		int mid=(k2+k3)>>1;
		bud(k1*2,k2,mid),bud(k1*2+1,mid+1,k3);
		upd1(k1);
		sum[k1]=sum[k1*2]+qry(k1*2+1,mid+1,k3,mx[k1*2]);
	}
	void mdf(int k1,int k2,int k3,int k4){
		if(k2==k3){
			if(pd){
				mx[k1]=h[n-k2+1];
				sum[k1]=a[n-k2+1];
			}else{
				mx[k1]=h[k2];
				sum[k1]=a[k2];
			}
			return;
		}
		int mid=(k2+k3)>>1;
		if(k4<=mid)mdf(k1*2,k2,mid,k4);else mdf(k1*2+1,mid+1,k3,k4);
		upd1(k1);
		sum[k1]=sum[k1*2]+qry(k1*2+1,mid+1,k3,mx[k1*2]);
	}
	void ask(int k1,int k2,int k3,int k4,int k5){
		if(k2>k5||k3<k4)return;
		if(k4<=k2&&k3<=k5){
			res+=qry(k1,k2,k3,mxx);
			mxx=max(mxx,mx[k1]);
			return;
		}
		int mid=(k2+k3)>>1;
		ask(k1*2,k2,mid,k4,k5),ask(k1*2+1,mid+1,k3,k4,k5);
	}
}A,B;

int main(){
	scanf("%d%d",&n,&Q);
	rep(i,1,n)scanf("%d",&h[i]);
	rep(i,1,n)scanf("%d",&a[i]);
	B.pd=1;
	A.bud(1,1,n),B.bud(1,1,n);
	while(Q--){
		int k1,k2,k3;scanf("%d%d%d",&k1,&k2,&k3);
		if(k1==1){
			a[k2]=k3;
			A.mdf(1,1,n,k2);
			B.mdf(1,1,n,n-k2+1);
		}else{
			if(k2<k3){
				mxx=0,res=0;
				B.ask(1,1,n,n-k3+1,n-(k2+1)+1);
				if(h[k2]>mxx){
					res+=a[k2];
				}else res=-1;
				printf("%lld\n",res);
			}else if(k2>k3){
				mxx=0,res=0;
				A.ask(1,1,n,k3,k2-1);
				if(h[k2]>mxx){
					res+=a[k2];
				}else res=-1;
				printf("%lld\n",res);
			}else{
				printf("%d\n",a[k2]);
			}
		}
	}
	return 0;
}

LCMCONST

这题拿了一血,好开心啊。

考虑把质数分开来考虑,对于每一个质数,发现只有次数相同的连通块是不确定的,对于次数相同的连通块,折半搜索,fwt 算贡献

#include<bits/stdc++.h>
#define D(...) fprintf(stderr,__VA_ARGS__)
//#define int long long
#define LL long long
#define MP make_pair
#define PB push_back
#define fi first
#define se second
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef pair<int,int>PII;typedef vector<int>VI;typedef vector<PII>VII;
template<typename T>void rd(T&x){int f=0,c;while(!isdigit(c=getchar()))f^=!(c^45);x=(c&15);while(isdigit(c=getchar()))x=x*10+(c&15);if(f)x=-x;}
template<typename T>void pt(T x,int c=-1){if(x<0)putchar('-'),x=-x;if(x>9)pt(x/10);putchar(x%10+48);if(c!=-1)putchar(c);}
template<typename T>void umin(T&x,const T&y){if(y<x)x=y;}
const int M=10005,P=1000000007;
int T,n,m,len,p[7],X[M],Y[M];
bool used[40];
struct Num{
	int e[7];bool bad;
	void clear(){memset(e,0,sizeof(e)),bad=0;}
	bool operator!=(const Num&b)const{
		rep(i,1,len)if(e[i]!=b.e[i])return 1;
		return 0;
	}
	void push(int k1,int k2){
		int pid=-1;
		rep(k,1,len)if(p[k]==k1)pid=k;
		if(pid==-1)p[pid=++len]=k1,assert(len<=5);
		e[pid]+=k2;
	}
	void umin(const Num&b){
		if(bad)*this=b;
		else{
			rep(i,1,len)::umin(e[i],b.e[i]);
		}
	}
}A[M],mx[M],G[40][40];
namespace xay5421{
	int num[40],pid;
	int f[(1<<19)+5],lim;
	int in1[40],in2[40];
	VI nd;
	void dfs(int k1){
		num[k1]=114514,nd.PB(k1);
		rep(j,1,n)if(G[k1][j].bad==0&&mx[k1].e[pid]==mx[j].e[pid]&&num[j]==-1){
			dfs(j);
		}
	}
	void fwt_and(int*a,int lim){
		for(int i=1;i<lim;i<<=1)for(int j=0;j<lim;j+=i<<1)rep(k,0,i-1){
			a[j+k]+=a[i+j+k];
			if(a[j+k]>=P)a[j+k]-=P;
		}
	}
	int fun(int pid){
		xay5421::pid=pid;
		memset(in1,-1,sizeof(in1));
		memset(in2,-1,sizeof(in2));
		memset(num,-1,sizeof(num));
		rep(i,1,m){
			if(mx[X[i]].e[pid]<mx[Y[i]].e[pid]){
				num[Y[i]]=A[i].e[pid];
			}else if(mx[X[i]].e[pid]>mx[Y[i]].e[pid]){
				num[X[i]]=A[i].e[pid];
			}
		}
		nd.clear();
		int res=1;
		rep(k1,1,n)if(num[k1]==-1){
			dfs(k1);
			int mid=SZ(nd)/2,now=mx[k1].e[pid];// [0,mid),[mid,SZ(nd))
			rep(i,0,mid-1)in1[nd[i]]=i;
			rep(i,mid,SZ(nd)-1)in2[nd[i]]=i-mid;
			VI eid1,eid2,eid3;
			rep(i,1,m){
				if(in1[X[i]]!=-1&&in1[Y[i]]!=-1)eid1.PB(i);else
				if(in2[X[i]]!=-1&&in2[Y[i]]!=-1)eid2.PB(i);else
				if((in1[X[i]]!=-1&&in2[Y[i]]!=-1)||(in2[X[i]]!=-1&&in1[Y[i]]!=-1))eid3.PB(i);
			}
			auto chk1=[&](int s)->bool{
				for(auto i:eid1){
					if(!((s>>in1[X[i]]&1)|(s>>in1[Y[i]]&1))){
						return 0;
					}
				}
				return 1;
			};
			auto chk2=[&](int s)->bool{
				for(auto i:eid2){
					if(!((s>>in2[X[i]]&1)|(s>>in2[Y[i]]&1))){
						return 0;
					}
				}
				return 1;
			};
			rep(i,0,(1<<mid)-1){
				if(chk1(i)){
					f[i]=1;
					rep(j,0,mid-1)if(~i>>j&1)f[i]=1LL*f[i]*now%P;
				}else f[i]=0;
			}
			fwt_and(f,1<<mid);
			int sz=SZ(nd)-mid;
			int cur=0;
			rep(s,0,(1<<sz)-1){
				if(chk2(s)){
					int t=0;
					for(auto i:eid3){
						int k1=X[i],k2=Y[i];
						if(in1[k1]==-1)swap(k1,k2);
						if(~s>>in2[k2]&1){
							if(~t>>in1[k1]&1)t^=1<<in1[k1];
						}
					}
					int x=f[t];
					rep(j,0,sz-1)if(~s>>j&1)x=1LL*x*now%P;
					(cur+=x)%=P;
				}
			}
			res=1LL*res*cur%P;
			rep(i,0,mid-1)in1[nd[i]]=-1;
			rep(i,mid,SZ(nd)-1)in2[nd[i]]=-1;
		}
		return res;
	}
}
signed main(){
	rd(T);
	int ans=1;
	while(T--){
		rd(n),rd(m);len=0;
		rep(i,1,n)mx[i].bad=1;
		rep(i,1,n)rep(j,1,n)G[i][j].bad=1;
		bool flg=0;
		rep(i,1,m){
			A[i].clear();
			rd(X[i]),rd(Y[i]);
			if(X[i]>Y[i])swap(X[i],Y[i]);
			int tmp;rd(tmp);
			while(tmp--){
				int k1,k2;rd(k1),rd(k2);
				A[i].push(k1,k2);
			}
			mx[X[i]].umin(A[i]);
			mx[Y[i]].umin(A[i]);
			if(G[X[i]][Y[i]].bad==1)G[X[i]][Y[i]]=G[Y[i]][X[i]]=A[i];
			else if(G[X[i]][Y[i]]!=A[i])flg=1;
		}
		rep(i,1,m)rep(j,1,len)if(mx[X[i]].e[j]!=A[i].e[j]&&mx[Y[i]].e[j]!=A[i].e[j])flg=1;
		if(flg){puts("0");goto GG;}
		rep(i,1,n)if(mx[i].bad==1){puts("-1");goto GG;}
		ans=1;
		rep(i,1,len)ans=1LL*ans*xay5421::fun(i)%P;
		printf("%d\n",ans);
		GG:;
	}
	return 0;
}

WEIRDMUL

发现这个 W 的计算和哈希很像,先用哈希的方法搞出后缀和,把 W(i,j) 转化为 sum[i]-sum[j+1]*pw[j-i+1],然后再稍加处理,把 W(i,j) 乘上 pw[i],这个乘的次数是可以算的,到最后除掉即可,于是 W(i,j) 变成了 sum[i]*pw[i]-sum[j+1]*pw[j+1],然后令 a[i]=sum[i]*pw[i],我们要求的

\[\prod_{i<j}(a[i]-a[j]) \]

由于原来求的式子里有个平方,所以可以变成更友好的形式

\[\prod_{i\neq j}(a[i]-a[j]) \]

我们令

\[F_j(x)=\prod_{i\neq j}(x-a[i]) \]

我们要求的是所有的 i 的 \(F_i(a[i])\),然后乘积一下

发现这个过程和多项式多点求值十分相似,魔改一下板子就行了,我写的常数有点大,要卡卡

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
#define SZ(x) ((int)(x).size())
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
#define D(...) fprintf(stderr,__VA_ARGS__)
//#define D(...) ((void)0)
template<typename T>void rd(T&x){int f=0,c;while(!isdigit(c=getchar()))f^=!(c^45);x=(c&15);while(isdigit(c=getchar()))x=x*10+(c&15);if(f)x=-x;}
const int P=998244353;typedef vector<int>poly;
vector<poly>p;poly ans;poly a;
inline int fpow(int a,int b){int res=1;for(;b;b>>=1,a=1LL*a*a%P)if(b&1)res=1LL*res*a%P;return res;}inline int add(int x,int y){x+=y;if(x>=P)x-=P;return x;}inline int sub(int x,int y){x-=y;if(x<0)x+=P;return x;}inline int mul(int x,int y){return 1LL*x*y%P;}
//void print(const poly&a,const string s=""){
//	cerr<<s<<":";rep(i,0,SZ(a)-1)D("%d ",a[i]);D("\n");
//}
namespace NTT{
	int base=1,root=-1,maxbase=-1;std::vector<int>roots={0,1},rev={0,1};
	void init(){int tmp=P-1;maxbase=0;while(!(tmp&1)){tmp>>=1,maxbase++;}root=2;while(1){if(fpow(root,1<<maxbase)==1&&fpow(root,1<<(maxbase-1))!=1)break;root++;}}
	void ensure_base(int nbase){if(maxbase==-1)init();if(nbase<=base)return;rev.resize(1<<nbase);for(int i=1;i<(1<<nbase);++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(nbase-1));roots.resize(1<<nbase);while(base<nbase){int z=fpow(root,1<<(maxbase-base-1));for(int i=(1<<(base-1));i<(1<<base);++i)roots[i<<1]=roots[i],roots[i<<1|1]=mul(roots[i],z);base++;}}
	void dft(std::vector<int>&a){int n=a.size(),zeros=__builtin_ctz(n);ensure_base(zeros);int shift=base-zeros;for(int i=0;i<n;++i)if(i<(rev[i]>>shift))std::swap(a[i],a[rev[i]>>shift]);for(int mid=1;mid<n;mid<<=1)for(int i=0;i<n;i+=(mid<<1))for(int j=0;j<mid;++j){int x=a[i+j],y=mul(a[i+j+mid],roots[mid+j]);a[i+j]=add(x,y);a[i+j+mid]=sub(x,y);}}
	std::vector<int>pmul(std::vector<int>a,std::vector<int>b){int need=a.size()+b.size()-1,nbase=0;while((1<<nbase)<need)nbase++;ensure_base(nbase);int size=1<<nbase;a.resize(size);b.resize(size);dft(a);dft(b);int inv=fpow(size,P-2);for(int i=0;i<size;++i)a[i]=mul(a[i],mul(b[i],inv));std::reverse(a.begin()+1,a.end());dft(a);a.resize(need);return a;}
}
using NTT::pmul;
inline void ntt(poly&a,int g,int lim){
	a.resize(lim);
	for(int i=0,j=0;i<lim;++i){if(i>j)swap(a[i],a[j]);for(int k=lim>>1;(j^=k)<k;k>>=1);}
	static poly w;w.resize(lim>>1);
	for(int i=1;i<lim;i<<=1){
		for(int j=w[0]=1,wn=fpow(g,(P-1)/(i<<1));j<i;++j)w[j]=1ll*w[j-1]*wn%P;
		for(int j=0;j<lim;j+=i<<1){
			for(int k=0;k<i;++k){
				int x=a[j+k],y=1ll*a[i+j+k]*w[k]%P;
				a[j+k]=(x+y)%P,a[i+j+k]=(x-y+P)%P;
			}
		}
	}
	if(g==332748118)for(int i=0,inv=fpow(lim,P-2);i<lim;++i)a[i]=1ll*a[i]*inv%P;
}
inline int getlim(int x){int n=1;while(n<=x)n<<=1;return n;}
inline poly pinv(const poly&a,int n=-1){
	if(n==-1)n=a.size();if(n==1)return poly(1,fpow(a[0],P-2));
	poly ans(pinv(a,(n+1)>>1)),tmp(&a[0],&a[0]+n);
	int lim=getlim(n*2-2);
	ntt(ans,3,lim),ntt(tmp,3,lim);
	for(int i=0;i<lim;++i)ans[i]=1ll*ans[i]*(2-1ll*ans[i]*tmp[i]%P+P)%P;
	ntt(ans,332748118,lim);
	return ans.resize(n),ans;
}
inline void pdiv(const poly&a,const poly&b,poly&d,poly&r){
	if(b.size()>a.size())return d.clear(),r=a,void();
	poly A(a),B(b),invB;int n=a.size(),m=b.size();
	reverse(A.begin(),A.end()),reverse(B.begin(),B.end());
	B.resize(n-m+1),invB=pinv(B,n-m+1);
	d=pmul(A,invB),d.resize(n-m+1),reverse(d.begin(),d.end());
	r=pmul(b,d);for(int i=0;i<m-1;++i)r[i]=(a[i]-r[i]+P)%P;r.resize(m-1);
}
inline void evaluate_init(int u,int l,int r){
	if(l==r)return p[u]=(poly){P-a[l],1},void();int mid=(l+r)>>1;
	evaluate_init(u<<1,l,mid),evaluate_init(u<<1|1,mid+1,r);
	p[u]=pmul(p[u<<1],p[u<<1|1]);
}
poly A[100];
inline void evaluate(int u,int l,int r,const poly&f,int dep){
	if(r-l+1<=512){
//		D("%d %d\n",l,r);
		for(int i=l;i<=r;++i){
			int x=0;
			for(int j=SZ(f)-1;j>=0;--j)x=(1ll*x*a[i]%P+f[j])%P;
			rep(j,l,r)if(j!=i)x=1ll*x*(a[i]-a[j])%P;
			ans[i]=(x+P)%P;
		}
		return;
	}
	poly tmp;
	A[dep]=pmul(f,p[u<<1]);
	pdiv(A[dep],p[u<<1|1],tmp,tmp);
	int mid=(l+r)>>1;
	evaluate(u<<1|1,mid+1,r,tmp,dep+1);
	A[dep]=pmul(f,p[u<<1|1]);
	pdiv(A[dep],p[u<<1],tmp,tmp);
	evaluate(u<<1,l,mid,tmp,dep+1);
}
inline void evaluate(){
	p.resize(SZ(a)<<2),evaluate_init(1,0,SZ(a)-1);
	ans.resize(SZ(a)),evaluate(1,0,SZ(a)-1,{1},1);
}
int T,n,X;
int main(){
//	freopen("a.in","r",stdin);
	rd(T);
	while(T--){
		rd(n),rd(X);
		a.resize(n+1);a[n]=0;
		rep(i,0,n-1)rd(a[i]);
		per(i,n-1,0)a[i]=(1LL*a[i+1]*X+a[i])%P;
		int pw=1;
		rep(i,0,n-1){
			a[i]=1LL*a[i]*pw%P;
			pw=1LL*pw*X%P;
		}
		int res=1,tmp=0;
		sort(a.begin(),a.end());
		rep(i,0,n-1)if(a[i]==a[i+1]){puts("0");goto GG;}
		evaluate();
		rep(i,0,SZ(ans)-1)res=1LL*res*ans[i]%P;
		rep(i,0,n-1)(tmp+=1LL*i*(n-i)%(P-1))%=P-1;
		res=1LL*res*fpow(fpow(X,P-2),tmp*2)%P;
		if((1LL*n*(n+1)/2)&1)res=-res;
		printf("%d\n",(res+P)%P);GG:;
	}
	return 0;
}

EXPREP

在末尾加上一个特殊字符,观察一下发现,要求的其实是 \(\sum_{1<=i<=n,i<=j<=n+1}(sum[j-1]-sum[i-1])(lcp(i,j)+1)\)

然后因为后缀自动机上的 lca 就是两个串的 lcs,所以在倒串上搞搞,具体的,其实是求每个状态的 endpos 集合两两 sum 的差,SAM 上线段树合并一下

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
const int N=1000005,M=N*40,P=998244353;
int T,n,wei[26],sum[N];char s[N];
int fpow(int k1,int k2){
	int k3=1;
	for(;k2;k2>>=1,k1=1LL*k1*k1%P)if(k2&1)k3=1LL*k3*k1%P;
	return k3;
}
struct SAM{
	int lst,cnt,fa[N],ch[N][27],len[N],pos[N],c[N],a[N],ans[N];
	int ind,rt[N],lc[M],rc[M],val[M],tot[M];
	void clear(){
		rep(i,1,cnt){
			fa[i]=0,memset(ch[i],0,sizeof(ch[i]));
			len[i]=pos[i]=c[i]=a[i]=ans[i]=rt[i]=0;
		}
		rep(i,1,ind){
			lc[i]=rc[i]=val[i]=tot[i]=0;
		}
		lst=cnt=1,len[1]=1;//
		ind=0;
	}
	void push(int c,int _pos){
		int p=lst,np=lst=++cnt;len[np]=len[p]+1,pos[np]=_pos;
		for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
		if(!p){fa[np]=1;return;}
		int q=ch[p][c];
		if(len[p]+1==len[q]){fa[np]=q;return;}
		int nq=++cnt;len[nq]=len[p]+1;
		memcpy(ch[nq],ch[q],sizeof(ch[q]));
		fa[nq]=fa[q],fa[q]=fa[np]=nq;
		for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
	}
	void mdf(int&k1,int k2,int k3,int k4,int&res){
		if(!k1)k1=++ind;
		++tot[k1],(val[k1]+=sum[k4])%=P;
		if(k2==k3)return;
		int mid=(k2+k3)>>1;
		if(k4<=mid){
			(res+=val[rc[k1]]-1LL*tot[rc[k1]]*sum[k4]%P+P)%=P;
			mdf(lc[k1],k2,mid,k4,res);
		}else{
			(res+=1LL*tot[lc[k1]]*sum[k4]%P-val[lc[k1]]+P)%=P;
			mdf(rc[k1],mid+1,k3,k4,res);
		}
	}
	void mer(int&k1,int k2,int l,int r,int&res){
		if(!k2)return;
		if(!k1){
			k1=k2;
			return;
		}
		if(l==r){
			return;
		}
		int mid=(l+r)>>1;
//		assert(res>=0);
		(res+=(1LL*val[rc[k1]]*tot[lc[k2]]%P-1LL*tot[rc[k1]]*val[lc[k2]]%P+P)%P)%=P;
		(res+=(1LL*tot[lc[k1]]*val[rc[k2]]%P-1LL*val[lc[k1]]*tot[rc[k2]]%P+P)%P)%=P;
//		assert(res>=0);
		mer(lc[k1],lc[k2],l,mid,res);
		mer(rc[k1],rc[k2],mid+1,r,res);
		val[k1]=(val[lc[k1]]+val[rc[k1]])%P;
		tot[k1]=(tot[lc[k1]]+tot[rc[k1]])%P;
	}
	void sol(){
		rep(i,1,cnt)++c[len[i]];
		rep(i,1,cnt)c[i]+=c[i-1];
		rep(i,1,cnt)a[c[len[i]]--]=i;
		int res=0;
		per(i,cnt,1){
			int k1=a[i];
			if(pos[k1]){
				mdf(rt[k1],1,cnt,pos[k1],ans[k1]);
			}
			if(k1!=1){
				mer(rt[fa[k1]],rt[k1],1,cnt,ans[fa[k1]]);
			}
//			printf("%d %d %d\n",k1,pos[k1],(P-ans[k1])%P);
			(res+=1LL*ans[k1]*(len[k1]-/*len[fa[k1]]*/0)%P)%=P;
		}
		printf("%lld\n",1LL*(P-res)*fpow(1LL*n*(n-1)/2%P,P-2)%P);
	}
}A;
int main(){
//	freopen("a.in","r",stdin);
//	freopen("d.out","w",stdout);
	scanf("%d",&T);
	while(T--){
		scanf("%s",s+1);n=strlen(s+1),s[++n]='z'+1;
		rep(i,0,25)scanf("%d",&wei[i]);
		rep(i,1,n)sum[i]=(sum[i-1]+wei[s[i]-'a'])%P;
		per(i,n,1)sum[i]=sum[i-1];
		reverse(s+1,s+1+n);
		reverse(sum+1,sum+1+n);
		A.clear();
		rep(i,1,n)A.push(s[i]-'a',i);
		A.sol();
	}
	return 0;
}

EXPTREES

是个有趣的题目,我们首先可以求出 \(f_i\) 表示有 i 条初始边的生成树的个数,然后由于初始边数相同,期望也是一样的,可以算一下贡献

对于求 f 的过程,是个矩阵树定理,不过稍稍修改,变成多项式的运算,但是这样复杂度略大,考虑带入插值进去,然后高消求解系数

对于算有 i 条边的期望的过程,考虑其指数型生成函数,是

\[\left(\frac{e^x+e^{-x}}{2}\right)^i\left(\frac{e^x-e^{-x}}{2}\right)^{n-1-i}e^{n(n-1)/2-n+1} \]

然后把这个东西的 e^i 的系数预处理出来,这样每次询问的复杂度就是 O(n) 了

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
#define D(...) fprintf(stderr,__VA_ARGS__)
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
typedef long long LL;
const int N=105,P=1e9+7,I2=(P+1)/2;
int n,m,q,ans[N],f[N*2],g[N*2],h[N*2];bool G[N][N];
int fpow(int k1,LL k2){
	int k3=1;for(;k2;k2>>=1,k1=1LL*k1*k1%P)if(k2&1)k3=1LL*k3*k1%P;return k3;
}
namespace xay5421{
	int A[N][N],B[N][N];
	int det(){
		int res=1;
		rep(i,1,n-1){
			int pos=-1;
			rep(j,i,n-1)if(A[i][j]){pos=j;break;}
			if(pos==-1)return 0;
			swap(A[i],A[pos]),res=-res;
			int I=fpow(A[i][i],P-2);
			rep(j,1,n-1)if(i!=j&&A[j][i]){
				int t=1LL*A[j][i]*I%P;
				rep(k,1,n)(A[j][k]-=1LL*A[i][k]*t%P)%=P;
			}
			res=1LL*res*A[i][i]%P;
		}
		return res;
	}
	void sol(){
		rep(x,1,n){
			rep(i,1,n)rep(j,1,n)A[i][j]=0;
			rep(i,1,n)rep(j,1,n)if(i!=j){
				if(G[i][j]){
					(A[i][i]-=x)%=P;
					(A[i][j]+=x)%=P;
				}else{
					(A[i][i]-=1)%=P;
					(A[i][j]+=1)%=P; 
				}
			}
			B[x][1]=1;
			rep(i,2,n){
				B[x][i]=1LL*B[x][i-1]*x%P;
			}
			B[x][n+1]=det();
		}
		rep(i,1,n){
			int pos=-1;
			rep(j,i,n)if(B[i][j]){pos=j;break;}
			assert(pos!=-1);
			swap(B[i],B[pos]);
			int I=fpow(B[i][i],P-2);
			rep(j,1,n)if(i!=j&&B[j][i]){
				int t=1LL*B[j][i]*I%P;
				rep(k,1,n+1)(B[j][k]-=1LL*B[i][k]*t%P)%=P;
			}
		}
		rep(i,1,n)ans[i-1]=1LL*B[i][n+1]*fpow(B[i][i],P-2)%P,(ans[i-1]+=P)%=P;
	}
}
int main(){
	scanf("%d%d%d",&n,&m,&q);
	rep(i,1,m){
		int k1,k2;scanf("%d%d",&k1,&k2);
		G[k1][k2]=G[k2][k1]=1;
	}
	xay5421::sol();
	rep(a,0,n-1)if(ans[a]){
		memset(f,0,sizeof(f));
		f[0+N]=1;
		rep(_,1,a){
			memcpy(g,f,sizeof(g));
			memset(f,0,sizeof(g));
			rep(i,-n+1,n-1){
				(f[i-1+N]+=1LL*g[i+N]*I2%P)%=P;
				(f[i+1+N]+=1LL*g[i+N]*I2%P)%=P;
			}
		}
		rep(_,1,n-1-a){
			memcpy(g,f,sizeof(g));
			memset(f,0,sizeof(f));
			rep(i,-n+1,n-1){
				(f[i-1+N]+=P-1LL*g[i+N]*I2%P)%=P;
				(f[i+1+N]+=1LL*g[i+N]*I2%P)%=P;
			}
		}
		rep(i,-n+1,n-1){
			f[i+N]=1LL*f[i+N]*ans[a]%P;
			(h[i+N]+=f[i+N])%=P;
		}
	}
	const int K=n*(n-1)/2-n+1,KK=1LL*n*(n-1)/2;
	while(q--){
		LL T;scanf("%lld",&T);
		int res=0;
		rep(i,-n+1,n-1){
			(res+=1LL*h[i+N]*fpow((i+K+P)%P,T%(P-1))%P)%=P;
		}
		res=1LL*res*fpow(KK,1LL*T%(P-1)*(P-2)%(P-1))%P;
		printf("%d\n",res);
	}
	return 0;
}
posted @ 2020-07-13 22:19  xay5421  阅读(223)  评论(1编辑  收藏  举报