luogu P5245 【模板】多项式快速幂

18 篇文章 0 订阅

背景:

多项式全家桶 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 &ThinSpace;&ThinSpace; 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]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值