Luogu7431 小 L 的计算题

发布时间 2023-09-07 10:58:46作者: yspm

Description

现有一个长度为 \(n\) 的非负整数数组 \(\{a_i\}\) 。小 L 定义了一种神奇变换:

\[f_k=\left(\sum_{i=1}^na_i^k\right)\bmod 998244353 \]

小 L 计划用变换生成的序列 \(f\) 做一些有趣的事情,但是他并不擅长算乘法,所以来找你帮忙,希望你能帮他尽快计算出 \(f_{1\dots n}\)

保证输入的 \(a_i\) 满足 \(0\le a_i\le10^9\)。在一个测试文件中,保证有 \(\sum n\le 4\times 10^5\)

Solution

很久没写多项式题了,所以这题用来写写板子。

题其实没啥难的,乍一看是求若干 \(f_k\) 的异或和,但是你需要的信息还是网格状的,所以你变换一下考量的维度,就能发现每个 \(i\) 你需要所有的次幂和。

等比数列求和,得到一个封闭,封闭形式展开,你就又得到一行对答案的贡献

再看看发现你把这个封闭都加起来再一块展开就行了,突出一个并行计算。那么你发现到现在你需要通分一下。

最最直观的想法是对于每个 \(i\)\(\displaystyle [x^i]\sum_{i=1}^n \frac{1}{1-a_ix}\),我拿我的板子写了一下这个分治发现死活过不去。

点开洛谷题解发现还有另外一种刻画的方法,就是你求 \([x^{i-1}]F(x)\) ,也就是把系数往小平移一位。这时候你表达一下通分的式子就是

\[\dfrac{\sum_{i=1}^n a_i\prod_{j\neq i}1-a_jx}{\prod_{i=1}^n(1-a_ix)} \]

他们说分母求导之后取负就是分母,我也觉得很神奇,不知道是不是我的表达式方式有问题导致把这个错过了。这样你省略了分治乘法中维护分子的部分,常数削减至原来的不多于四成,可以通过。

Code

因为没有缺省源了,所以放完整代码。

#include<bits/stdc++.h>
#define fir first
#define sec second
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define vi vector<int> 
using namespace std;
template<typename T>inline void ckmax(T &x,T y){x=x<y?y:x;}
template<typename T>inline void ckmin(T &x,T y){x=x>y?y:x;}
template<typename T=int>inline T read(){
	T res=0,f=1; char k;
	while(!isdigit(k=getchar())) if(k=='-') f=-1;
	while(isdigit(k)) res=res*10+k-'0',k=getchar();
	return res*f;
}
template<typename T>inline void print(T x,bool fl=1){
	if(x<0) putchar('-'),x=-x; 
	if(x>=10) print(x/10,0);
	putchar(x%10+'0');
	if(fl) putchar('\n');
}
const int N=1e6+10;
const int mod=998244353;
inline int add(int x,int y,int Mod=mod){return x+y>=Mod?x+y-Mod:x+y;}
inline int del(int x,int y,int Mod=mod){return x-y<0?x-y+Mod:x-y;}
inline int mul(int x,int y,int Mod=mod){return 1ll*x*y-1ll*x*y/Mod*Mod;}
inline void ckadd(int &x,int y,int Mod=mod){x=x+y>=Mod?x+y-Mod:x+y;}
inline void ckdel(int &x,int y,int Mod=mod){x=x-y<0?x-y+Mod:x-y;}
inline void ckmul(int &x,int y,int Mod=mod){x=1ll*x*y-1ll*x*y/Mod*Mod;}
inline int ksm(int x,int y,int Mod=mod){int res=1; for(;y;y>>=1,ckmul(x,x,Mod)) if(y&1) ckmul(res,x,Mod); return res;}
int r[N],W[N],inv[N],n,a[N];
inline void NTT(vi &f,int lim,int opt){
	f.resize(lim);
	rep(i,0,lim-1){
		r[i]=r[i>>1]>>1|((i&1)?(lim>>1):0);
		if(i<r[i]) swap(f[i],f[r[i]]);
	}
	for(int p=2;p<=lim;p<<=1){
		int len=p>>1;
		W[0]=1; W[1]=ksm(3,(mod-1)/p);
		if(opt==-1) W[1]=ksm(W[1],mod-2);
		rep(j,2,len-1) W[j]=mul(W[j-1],W[1]);
		for(int k=0;k<lim;k+=p){
			for(int l=k;l<k+len;++l){
				int tt=mul(f[l+len],W[l-k]);
				f[l+len]=del(f[l],tt);
				ckadd(f[l],tt);
			}
		}
	}
	if(opt==-1){
		int invl=ksm(lim,mod-2);
		rep(i,0,lim-1) ckmul(f[i],invl);
	}
}
inline vi Mul(vi A,vi B){
	int n=A.size(),m=B.size();
	int lim=1;
	while(lim<=(n+m)) lim<<=1;
	NTT(A,lim,1);
	NTT(B,lim,1);
	rep(i,0,lim-1) ckmul(A[i],B[i]);
	NTT(A,lim,-1);
	A.resize(n+m-1);
	return A;
}
inline vi Plus(vi A,vi B){
	int n=A.size(),m=B.size();
	if(n<m) swap(n,m),swap(A,B);
	for(int i=0;i<m;++i) ckadd(A[i],B[i]);
	return A;
}
inline vi conquer(int l,int r){
	if(l==r) return {1,del(0,a[l])};
	int mid=(l+r)>>1;
	return Mul(conquer(l,mid),conquer(mid+1,r));
}
inline vi Inv(vector<int> f,int len){
	if(len==1) return {ksm(f[0],mod-2)};
	vector<int> G0=Inv(f,(len+1)>>1);
	int lim=1; while(lim<=(len<<1)) lim<<=1;
	vector<int> tmp,G;
	rep(i,0,len-1) tmp.push_back(f[i]);
	NTT(tmp,lim,1); NTT(G0,lim,1);
	rep(i,0,lim-1) G.push_back(mul(G0[i],del(2,mul(G0[i],tmp[i]))));
	NTT(G,lim,-1); G.resize(len);
	return G;
}

signed main(){
	int T=read();
	while(T--){
		n=read();
		for(int i=1;i<=n;++i) a[i]=read();
		vi zi,mu=conquer(1,n);
		zi.resize(mu.size());
		for(int i=0;i<zi.size()-1;++i) zi[i]=del(0,mul(mu[i+1],i+1));
		int ans=0;
		zi=Mul(zi,Inv(mu,mu.size()));
		for(int i=0;i<n;++i) ans^=zi[i];
		print(ans);
	}
	return 0;
}