题目大意:
定义f与g的循环卷积,
(f∗g)(k)=∑k≡i+j(mod n)f(i)∗g(j)(f*g)(k)=\sum_{k≡i+j(mod\ n)}f(i)*g(j)(f∗g)(k)=k≡i+j(mod n)∑f(i)∗g(j)
给定nnn项的多项式AAA,BBB和常数CCC。
求A∗BCA*B^CA∗BC的所有项mod (n+1)mod\ (n+1)mod (n+1)的值。
其中,nnn只含小于101010的质因子,n+1n+1n+1为质数。
n≤5∗105n≤5*10^5n≤5∗105,C≤109C≤10^9C≤109。
分析:
因为n+1n+1n+1是质数,xn≡1x^n≡1xn≡1。
所以循环卷积得出来的多项式与一般卷积多项式的点值相同。
如果我们使用nnn个点值插出来的多项式就是循环卷积多项式。
发现nnn只含小于101010的质因子。
如果只含222这个质因子就是一般的ntt了,考虑有其他质因子怎么做。
举个例子,用555这个质因子来算。
我们把当前多项式变成
(a0+a5x5)+x(a1+a6x5)+x2(a2+a7x5)+x3(a3+a8x5)+x4(a4+a9x5)(a_0+a_5x^5)+x(a_1+a_6x^5)+x^2(a_2+a_7x^5)+x^3(a_3+a_8x^5)+x^4(a_4+a_9x^5)(a0+a5x5)+x(a1+a6x5)+x2(a2+a7x5)+x3(a3+a8x5)+x4(a4+a9x5)
即按余数分类。
分别算出a0+a5xa_0+a_5xa0+a5x,a1+a6xa_1+a_6xa1+a6x,……的点值。
显然此时要把点值要变成5次方,然而这些多项式点值自变量为wk/5iw_{k/5}^iwk/5i,5次方即为wkiw_{k}^iwki。
所以直接加上对应点值,再乘上当前单位复根的0~4次方。
一般ntt维护一个wnw_nwn和www,此时要用数组维护wn[i]w_n[i]wn[i],w[i]w[i]w[i]。表示单位复根的iii次方与当前自变量的iii次方即可。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
const int maxn=5e5+7;
using namespace std;
int n,tot;
int p[maxn],nump[4];
LL mod,G,C;
LL f[maxn],g[maxn],w[17],v[17],b[maxn];
LL power(LL x,LL y)
{
if (y==0) return 1;
if (y==1) return x;
LL c=power(x,y/2);
c=c*c%mod;
if (y&1) c=c*x%mod;
return c;
}
void prework()
{
int c=n;
while (c%2==0) c/=2,p[++tot]=2,nump[0]++;
while (c%3==0) c/=3,p[++tot]=3,nump[1]++;
while (c%5==0) c/=5,p[++tot]=5,nump[2]++;
while (c%7==0) c/=7,p[++tot]=7,nump[3]++;
for (int i=2;i<n;i++)
{
if ((nump[0]) && (power(i,n/2)==1)) continue;
if ((nump[1]) && (power(i,n/3)==1)) continue;
if ((nump[2]) && (power(i,n/5)==1)) continue;
if ((nump[3]) && (power(i,n/7)==1)) continue;
G=i;
break;
}
}
void change(LL *a,int l,int r,int dep) //暴力调顺序
{
if (l==r) return;
int len=(r-l+1)/p[dep];
for (int i=0;i<=r-l;i++)
{
int j=(i%p[dep])*len+i/p[dep];
b[l+j]=a[l+i];
}
for (int i=0;i<=r-l;i++) a[l+i]=b[l+i];
for (int i=0;i<p[dep];i++) change(a,l+i*len,l+(i+1)*len-1,dep+1);
}
void fft(LL *a,int f)
{
int len=1;
for (int i=tot;i>0;i--)
{
len*=p[i];
LL wn;
if (f==1) wn=power(G,n/len); //当前单位复根
else wn=power(G,n-n/len);
v[0]=1;
for (int j=1;j<p[i];j++) v[j]=(v[j-1]*wn)%mod; //j次方单位复根
for (int j=0;j<n;j++) b[j]=0; //记录当前层变化后的a
for (int j=0;j<n;j+=len)
{
for (int l=0;l<p[i];l++) w[l]=1; //当前自变量重置
for (int k=0;k<len;k++)
{
int num=len/p[i]; //小区间的大小
for (int l=0;l<p[i];l++)
{
b[j+k]=(b[j+k]+a[j+l*num+k%num]*w[l])%mod; //第k个自变量的值等于所有小区间内对应位置的值乘上对应次方的和。
w[l]=(w[l]*v[l])%mod; //自变量变为下一个
}
}
}
for (int i=0;i<n;i++) a[i]=b[i];
}
if (f==-1)
{
LL inv=power(n,mod-2);
for (int i=0;i<n;i++) a[i]=a[i]*inv%mod;
}
}
int main()
{
scanf("%d%lld",&n,&C);
mod=n+1;
for (int i=0;i<n;i++) scanf("%lld",&f[i]);
for (int i=0;i<n;i++) scanf("%lld",&g[i]);
prework();
change(f,0,n-1,1);
change(g,0,n-1,1);
fft(f,1);
fft(g,1);
for (int i=0;i<n;i++) f[i]=f[i]*power(g[i],C)%mod;
change(f,0,n-1,1);
fft(f,-1);
for (int i=0;i<n;i++) printf("%lld\n",f[i]);
}