題目
正解
隨便生成函數,顯然答案爲
外面的這個乘方可以直接快速冪,時間複雜度。
重點是如何計算裏面的這個東西。
由於,所以不同的的個數大概是,約等於。
將相同的一起計算,用個組合數就可以做到處理,然後花的時間來將不同的合併起來。
然而常數太大TLE了……
這時候可以想到當個數比較少的時候,直接暴力做。
定義一個閾值,理論上這個閾值設最優,實際上這隻有85分。
跑個大數據,然後手動三分一下閾值,發現當這個閾值設爲的時候跑得最快。
於是就過了……
當然上面那樣做是水法。
正解是一個見過一次就可能不會忘的小科技。
考慮這題本質上就是做一個長度爲的循環卷積。題目給出的顯然就是次單位根()。
如果我們求出了長度爲的,在這個上操作一波之後再回去,那就可以達到一個優秀的時間複雜度。
現在的問題是如何求這個東西:(注意我們平常求的要將長度湊到形式的,這樣算的循環卷積就是長度的循環卷積,和我們要求的不同)
記單位根爲(就是題目的),我們要求,其中
後面的顯然是個卷積,於是NTT即可(這個NTT和我們這裏說了求長度爲的DFT沒有什麼關係,不要混淆……)
不過有時候沒有平方根,那麼還有另一種拆分方式:
同樣可以求得。
至於,用代進去,最終結果乘即可。
對於這題,直接套用這個做法可以得到和暴力差不多的時間(除快速冪部分外,快速冪部分直接在點值上快速冪)。
這題有個更簡單的實現方式:對於一個,直接將代進去,就可以快速地計算出它的。然後它的乘方就直接在點值上乘方,接着將不同的按對應的位置乘在一起,再把每個位置取次方,最後回來。
於是真正需要實現上面這個算法的部分就只有一次啊……
代碼
水法
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;
}