6718. 【2020.06.12省選模擬】T1 Number

題目


正解

隨便生成函數,顯然答案爲((1+xti))m(\prod (1+x^{t_i}))^m
外面的這個乘方可以直接快速冪,時間複雜度O(elgelgm)O(e\lg e \lg m)
重點是如何計算裏面的這個東西。

由於ti2e7\sum t_i\leq2e7,所以不同的titi的個數大概是ti\sqrt {\sum t_i},約等於60006000
tit_i相同的一起計算,用個組合數就可以做到O()O(個數)處理,然後花O(tielge)O(\sqrt {\sum t_i}e\lg e)的時間來將tit_i不同的合併起來。
然而常數太大TLE了……
這時候可以想到當個數比較少的時候,直接暴力做。
定義一個閾值,理論上這個閾值設lge\lg e最優,實際上這隻有85分。
跑個大數據,然後手動三分一下閾值,發現當這個閾值設爲10001000的時候跑得最快。
於是就過了……

當然上面那樣做是水法。
正解是一個見過一次就可能不會忘的小科技。
考慮這題本質上就是做一個長度爲ee的循環卷積。題目給出的xx顯然就是ee次單位根(xe(modp)x^e\equiv \pmod p)。
如果我們求出了長度爲eeDFTDFT,在這個DFTDFT上操作一波之後再IDFTIDFT回去,那就可以達到一個優秀的時間複雜度。

現在的問題是如何求這個東西:(注意我們平常求的DFTDFT要將長度湊到2k2^k形式的,這樣算的循環卷積就是2k2^k長度的循環卷積,和我們要求的不同)
記單位根爲ωe\omega_e(就是題目的xx),我們要求F(ωek)F(\omega_e^k),其中k[0,e)k\in [0,e)
F(ωek)=i=0e1aiωik=i=0e1aiωek2+i2(ki)22=i=0e1aiω2ek2+i2(ki)2=ω2ek2i=0e1aiω2ei2ω2e(ki)2F(\omega_e^k)=\sum_{i=0}^{e-1}a_i\omega^{ik} \\ =\sum_{i=0}^{e-1} a_i\omega_e^{\frac{k^2+i^2-(k-i)^2}{2}} \\ =\sum_{i=0}^{e-1} a_i\omega_{2e}^{k^2+i^2-(k-i)^2} \\ =\omega_{2e}^{k^2}\sum_{i=0}^{e-1} a_i\omega_{2e}^{i^2}\omega_{2e}^{-(k-i)^2}
後面的顯然是個卷積,於是NTT即可(這個NTT和我們這裏說了求長度爲ee的DFT沒有什麼關係,不要混淆……)
不過有時候ωe\omega_e沒有平方根,那麼還有另一種拆分方式:
F(ωek)=i=0e1aiωik=i=0e1aiωeCk+i2Ck2Ci2F(\omega_e^k)=\sum_{i=0}^{e-1}a_i\omega^{ik} \\ =\sum_{i=0}^{e-1} a_i\omega_e^{C_{k+i}^2-C_k^2-C_i^2}
同樣可以求得。
至於IDFTIDFT,用ω1\omega^{-1}代進去,最終結果乘e1e^{-1}即可。

對於這題,直接套用這個做法可以得到和暴力差不多的時間(除快速冪部分外,快速冪部分直接在點值上快速冪)。
這題有個更簡單的實現方式:對於一個1+xti1+x^{t_i},直接將ωek,k[0,e)\omega_e^k,k\in[0,e)代進去,就可以快速地計算出它的DFTDFT。然後它的乘方就直接在點值上乘方,接着將tit_i不同的按對應的位置乘在一起,再把每個位置取mm次方,最後IDFTIDFT回來。
於是真正需要實現上面這個算法的部分就只有一次IDFTIDFT啊……


代碼

水法

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 1000010
#define mo 998244353
#define E 131072
#define T 20000000
#define ll long long
ll qpow(ll x,int y=mo-2){
	ll r=1;
	for (;y;y>>=1,x=x*x%mo)
		if (y&1)
			r=r*x%mo;
	return r;
}
int n,m,x,e;
int t[N];
int nN,re[E],w[E];
void setlen(int n){
	int bit=0;
	for (nN=1;nN<=n;nN<<=1,bit++);
	re[0]=0;
	for (int i=1;i<nN;++i)
		re[i]=re[i>>1]>>1|(i&1)<<bit-1;
}
void clear(ll A[],int n){
	memset(A,0,sizeof(ll)*n);
}
void dft(ll A[],int flag){
	for (int i=0;i<nN;++i)
		if (i<re[i])
			swap(A[i],A[re[i]]);
	for (int i=1;i<nN;i<<=1){
		ll wn=qpow(3,(flag==1?(mo-1)/(2*i):mo-1-(mo-1)/(2*i)));
		w[0]=1;
		for (int k=1;k<i;++k)
			w[k]=(ll)w[k-1]*wn%mo;
		for (int j=0;j<nN;j+=i<<1)
			for (int k=0;k<i;++k){
				ll x=A[j+k],y=w[k]*A[j+k+i];
				A[j+k]=(x+y)%mo;
				A[j+k+i]=(x-y+(ll)mo*mo)%mo;
			}
	}
	if (flag==-1){
		ll invn=qpow(nN);
		for (int i=0;i<nN;++i)
			A[i]=A[i]*invn%mo;
	}
}
void multi(ll c[],ll a[],ll b[],int n){
	static ll A[E],B[E];
	setlen(n*2);
	clear(A,nN);
	for (int i=0;i<n;++i)
		A[i]=a[i];
	dft(A,1);
	if (a!=b){
		clear(B,nN);
		for (int i=0;i<n;++i)
			B[i]=b[i];
		dft(B,1);
		for (int i=0;i<nN;++i)
			c[i]=A[i]*B[i]%mo;
	}
	else{
		for (int i=0;i<nN;++i)
			c[i]=A[i]*A[i]%mo;
	}
	dft(c,-1);
	for (int i=n;i<nN;++i){
		(c[i-n]+=c[i])%=mo;
		c[i]=0;
	}
}
ll fac[N],ifac[N];
ll C(int m,int n){return fac[m]*ifac[n]%mo*ifac[m-n]%mo;}
ll F[E],G[E],H[E];
struct Ans{
	int a,b;
} ans[N];
int cnt;
bool cmpa(Ans x,Ans y){return x.a<y.a;}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	freopen("number.in","r",stdin);
	freopen("number.out","w",stdout);
	scanf("%d%d%d",&n,&m,&x);
	for (int i=1,pw=x;i<=50000;++i,pw=(ll)pw*x%mo)
		if (pw==1){
			e=i;
			break;
		}
	fac[0]=1;
	for (int i=1;i<=n;++i)
		fac[i]=fac[i-1]*i%mo;
	ifac[n]=qpow(fac[n]);
	for (int i=n-1;i>=0;--i)
		ifac[i]=ifac[i+1]*(i+1)%mo;	
	for (int i=1;i<=n;++i)
		scanf("%d",&t[i]);
	sort(t+1,t+n+1);
	H[0]=1;
	int B=/*log2(e)*/1000;
	for (int i=1,j=1;i<=n;++i)
		if (t[i]!=t[i+1]){
			int l=i-j+1;
			if (l>B){
				for (int k=0;k<=l;++k)
					F[(ll)k*t[i]%e]+=C(l,k);
				multi(H,H,F,e);
				for (int k=0;k<=l;++k)
					F[(ll)k*t[i]%e]=0;
			}
			else{
				while (l--){
					for (int k=e-1;k>=0;--k)
						(H[k+t[i]]+=H[k])%=mo;
					for (int k=e-1+t[i];k>=e;--k)
						(H[k-e]+=H[k])%=mo,H[k]=0;
				}
			}
			j=i+1;
		}
	G[0]=1;
	for (;m;m>>=1,multi(H,H,H,e))
		if (m&1)
			multi(G,G,H,e);
	for (int i=0,pw=1;i<e;++i,pw=(ll)pw*x%mo){
		int a=pw,b=G[i];
		if (b)
			ans[++cnt]={a,b};
	}
	sort(ans+1,ans+cnt+1,cmpa);
	for (int i=1;i<=cnt;++i)
		printf("%d %d\n",ans[i].a,ans[i].b);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章