• 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,'
    ');
    	}
    	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,'
    ');
    		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,'
    ');
    	}
    	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
    ",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
    ",res);
    			}else{
    				printf("%d
    ",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
    ",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 eq j}(a[i]-a[j]) ]

    我们令

    [F_j(x)=prod_{i eq 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("
    ");
    //}
    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
    ",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
    ",(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
    ",k1,pos[k1],(P-ans[k1])%P);
    			(res+=1LL*ans[k1]*(len[k1]-/*len[fa[k1]]*/0)%P)%=P;
    		}
    		printf("%lld
    ",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} ight)^ileft(frac{e^x-e^{-x}}{2} ight)^{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
    ",res);
    	}
    	return 0;
    }
    
  • 相关阅读:
    C语言速记3(作用域,枚举)
    c语言static在java语言区别
    c语言速记2(存储类,运算符)
    寄存器,计数器
    C语言extern的概念(声明和定义的区别)
    c语言速记1(基本结构,编译运行,声明定义,类型,常量)
    硬盘分区的相关概念(主分区,扩展分区,逻辑分区,MBR,DBR)
    android源码场景1(环境配置)
    c#截取两个指定字符串中间的字符串(转载)
    toFixed、Math.round 的区别(转载)
  • 原文地址:https://www.cnblogs.com/xay5421/p/CCJULY20.html
Copyright © 2020-2023  润新知