背景:
多项式全家桶
eating...
\text{eating...}
eating...
题目传送门:
https://www.luogu.org/problemnew/show/P5245
题意:
给定一个多项式
F
(
x
)
F(x)
F(x),求一个多项式
G
(
x
)
≡
F
k
(
x
)
(
m
o
d
  
x
n
)
G(x)≡F^k(x)(\mod x^n)
G(x)≡Fk(x)(modxn)。
思路 1 1 1:
像普通快速幂一样,暴力跑
NTT
\text{NTT}
NTT。
时间复杂度:
Θ
(
n
log
n
log
k
)
\Theta(n\log n\log k)
Θ(nlognlogk)。
当然这道题
k
k
k实在巨大,是不可能过的。
有兴趣的可以尝试用这种方法卡掉:
luogu P5273
\text{luogu P5273}
luogu P5273【模板】多项式幂函数 (加强版)。
由于我的卡常方法实在有限,只能
20pts
\text{20pts}
20pts,
orz
\text{orz}
orz卡过的神仙。
卡过常的代码。
代码 1 1 1:
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<ctype.h>
#define LL long long
#define I inline
#define R register
#define inv(x) dg(x,mod-2)
const int mod=998244353,G=3,inv_G=332748118;
using namespace std;
int f[1000010],a[1000010],b[1000010],ans[1000010];
int limit,n,l,k,INV,r[1000010];
I char getc()
{
static char buf[1<<18],*fs,*ft;
return(fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<18,stdin)),fs==ft)?EOF:*fs++;
}
I int read()
{
char ch=getc(),f=1;
int x=0;
for(;!isgraph(ch);ch=getc());
if(ch=='-') f=-1,ch=getc();
for(;isdigit(ch);ch=getc())
x=((x+(x<<2))<<1)+(ch^0x30);
return x*f;
}
static const int BUF=50000000;
char buf[BUF],*h=buf;
I void put(char ch)
{
h==buf+BUF?(fwrite(buf,1,BUF,stdout),h=buf):0;
*h++=ch;
}
I void putint(int num)
{
static char _buf[30];
sprintf(_buf,"%d",num);
for(char *s=_buf;*s;s++)put(*s);
}
I void finish()
{
fwrite(buf,1,h-buf,stdout);
}
int dg(int x,int k)
{
int ans=1;
for(;k;k>>=1)
{
if(k&1) ans=(LL)ans*x%mod;
x=(LL)x*x%mod;
}
return ans;
}
I void NTT(int *now,int limit,int op)
{
for(R int i=0;i<limit;++i)
if(i<r[i]) swap(now[i],now[r[i]]);
for(R int mid=1;mid<limit;mid<<=1)
{
int wn=dg(op==1?G:inv_G,(mod-1)/(mid<<1));
for(R int j=0;j<limit;j+=(mid<<1))
{
int w=1;
for(R int k=0;k<mid;++k,w=((LL)w*wn)%mod)
{
int x=now[j+k],y=(LL)w*now[j+k+mid]%mod;
now[j+k]=(x+y)%mod;
now[j+k+mid]=(x-y+mod)%mod;
}
}
}
}
I void mul(int *f,int *g)
{
for(R int i=n;i<limit;++i)
a[i]=b[i]=0;
for(R int i=0;i<n;++i)
a[i]=f[i],b[i]=g[i];
NTT(a,limit,1),NTT(b,limit,1);
for(R int i=0;i<limit;++i)
a[i]=(LL)a[i]*b[i]%mod;
NTT(a,limit,-1);
for(R int i=0;i<n;++i)
f[i]=(LL)a[i]*INV%mod;
}
int main()
{
n=read(),k=read();
limit=1,l=0;
while(limit<(n<<1))
limit<<=1,++l;
for(R int i=1;i<limit;++i)
r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
INV=inv(limit);
for(R int i=0;i<n;++i)
f[i]=read();
ans[0]=1;
for(;k;k>>=1)
{
if(k&1) mul(ans,f);
mul(f,f);
}
for(R int i=0;i<n;++i)
putint(ans[i]),put(' ');
finish();
}
思路 2 2 2:
两边同时取
ln
\ln
ln,由换底公式可知,有:
ln
G
(
x
)
≡
k
ln
F
(
x
)
(
m
o
d
  
x
n
)
\ln G(x)≡k\ln F(x)(\mod x^n)
lnG(x)≡klnF(x)(modxn)
那就是对
F
F
F取
ln
\ln
ln,结果乘上
k
k
k,最后取
exp
\text{exp}
exp即可。
为什么是取
exp
\text{exp}
exp,因为:
G
(
x
)
=
e
ln
G
(
x
)
G(x)=e^{\ln G(x)}
G(x)=elnG(x)。
时间复杂度:
Θ
(
n
log
n
)
\Theta(n\log n)
Θ(nlogn)。
代码 2 2 2:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
const LL mod=998244353,G=3,inv_G=332748118;
using namespace std;
LL a[1000010],b[1000010],f[1000010],g[1000010],g1[1000010],g2[1000010],g3[1000010];
LL k;
char s[1000010];
int limit,n,l,r[1000010];
LL dg(LL x,LL k)
{
if(!k) return 1;
LL op=dg(x,k>>1);
if(k&1) return op*op%mod*x%mod; else return op*op%mod;
}
LL inv(LL x)
{
return dg(x,mod-2);
}
void dao(LL *f,LL *g,int n)
{
for(int i=1;i<n;i++)
g[i-1]=i*f[i]%mod;
g[n-1]=0;
}
void jifen(LL *f,LL *g,int n)
{
for(int i=1;i<n;i++)
g[i]=f[i-1]*inv(i)%mod;
g[0]=0;
}
void init(int n)
{
limit=1,l=0;
while(limit<(n<<1))
limit<<=1,l++;
for(int i=1;i<limit;i++)
r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
}
void NTT(LL *now,int limit,int op)
{
for(int i=0;i<limit;i++)
if(i<r[i]) swap(now[i],now[r[i]]);
for(int mid=1;mid<limit;mid<<=1)
{
LL wn=dg(op==1?G:inv_G,(mod-1)/(mid<<1));
for(int j=0;j<limit;j+=(mid<<1))
{
LL w=1;
for(int k=0;k<mid;k++,w=(w*wn)%mod)
{
LL x=now[j+k],y=w*now[j+k+mid]%mod;
now[j+k]=(x+y)%mod;
now[j+k+mid]=(x-y+mod)%mod;
}
}
}
}
void dft(LL *f,int n,int limit)
{
NTT(f,limit,-1);
LL INV=inv(limit);
for(int i=0;i<n;i++)
f[i]=f[i]*INV%mod;
}
void poly_inv(LL *f,LL *g,int n)
{
if(n==1)
{
g[0]=inv(f[0]);
return;
}
poly_inv(f,g,(n+1)>>1);
init(n);
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(int i=0;i<n;i++)
a[i]=f[i],b[i]=g[i];
NTT(a,limit,1),NTT(b,limit,1);
for(int i=0;i<limit;i++)
b[i]=b[i]*((2ll-a[i]*b[i]%mod+mod)%mod)%mod;
dft(b,n,limit);
for(int i=0;i<n;i++)
g[i]=b[i];
}
void poly_ln(LL *f,int n)
{
dao(f,g1,n);
poly_inv(f,g2,n);
init(n);
NTT(g1,limit,1),NTT(g2,limit,1);
for(int i=0;i<limit;i++)
g1[i]=g1[i]*g2[i]%mod;
dft(g1,n,limit);
jifen(g1,g2,n);
}
void poly_exp(LL *f,LL *g,int n)
{
if(n==1)
{
g[0]=1;
return;
}
poly_exp(f,g,(n+1)>>1);
memset(g1,0,sizeof(g1));
memset(g2,0,sizeof(g2));
poly_ln(g,n);
init(n);
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(int i=0;i<n;i++)
a[i]=g[i],b[i]=((LL)(!i)-g2[i]+f[i]+mod)%mod;
NTT(a,limit,1),NTT(b,limit,1);
for(int i=0;i<limit;i++)
a[i]=a[i]*b[i]%mod;
dft(a,n,limit);
for(int i=0;i<n;i++)
g[i]=a[i];
}
void poly_ksm(LL *f,LL *g,int n)
{
poly_ln(f,n);
for(int i=0;i<n;i++)
g3[i]=g2[i]*k%mod;
memset(g1,0,sizeof(g1));
memset(g2,0,sizeof(g2));
poly_exp(g3,g,n);
}
int main()
{
scanf("%d",&n);
scanf("%s",s+1);
int l=strlen(s+1);
for(int i=1;i<=l;i++)
k=(k*10+(s[i]-48))%mod;
for(int i=0;i<n;i++)
scanf("%lld",&f[i]);
poly_ksm(f,g,n);
for(int i=0;i<n;i++)
printf("%lld ",g[i]);
}