多项式全家桶

本文介绍了多项式的高级运算技巧,包括多项式开根、多项式对数、指数运算、快速幂及牛顿迭代法等。通过这些方法可以高效解决复杂的多项式问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

多项式开根:

要求一个g(x)g(x)g(x),使得

g2(x)≡f(x) (mod xn)(1) g^2(x)\equiv f(x)\ (mod\ x^n)\tag{1} g2(x)f(x) (mod xn)(1)

设找到一个t(x)t(x)t(x),满足

t2(x)≡f(x) (mod x⌈n2⌉) t^2(x) \equiv f(x)\ (mod\ x^{\lceil\frac{n}{2}\rceil}) t2(x)f(x) (mod x2n)

移项,平方得到

(t2(x)−f(x))2≡0 (mod xn) (t^{2}(x)-f(x))^2\equiv 0\ (mod\ x^{n}) (t2(x)f(x))20 (mod xn)

所以

(t2(x)+f(x))2≡4×t2(x)×f(x) (mod xn) (t^{2}(x)+f(x))^2\equiv 4\times t^{2}(x)\times f(x)\ (mod\ x^{n}) (t2(x)+f(x))24×t2(x)×f(x) (mod xn)

代入(1)(1)(1)式,得到

(t2(x)+f(x))2≡4×t2(x)×g2(x) (mod xn) (t^{2}(x)+f(x))^2\equiv 4\times t^{2}(x)\times g^2(x)\ (mod\ x^{n}) (t2(x)+f(x))24×t2(x)×g2(x) (mod xn)

化简有

(t2(x)+f(x)2×t(x))2≡g2(x) (mod xn) (\frac{t^{2}(x)+f(x)}{2\times t(x)})^2\equiv g^2(x)\ (mod\ x^{n}) (2×t(x)t2(x)+f(x))2g2(x) (mod xn)

t2(x)+f(x)2×t(x)≡g(x) (mod xn) \frac{t^{2}(x)+f(x)}{2\times t(x)}\equiv g(x)\ (mod\ x^{n}) 2×t(x)t2(x)+f(x)g(x) (mod xn)

边界情况如果保证[x0]f(x)=0[x^0]f(x)=0[x0]f(x)=0,那么可以直接有[x0]g(x)=1[x^{0}]g(x)=1[x0]g(x)=1,否则需要二次剩余求[x0]g(x)[x^0]g(x)[x0]g(x)

多项式lnlnln

要求一个g(x)g(x)g(x),使得

g(x)≡ln(f(x)) (mod xn) g(x)\equiv ln(f(x))\ (mod\ x^{n}) g(x)ln(f(x)) (mod xn)

两边求导,得到

g′(x)≡f′(x)f(x) (mod xn) g'(x)\equiv \frac{f'(x)}{f(x)}\ (mod\ x^{n}) g(x)f(x)f(x) (mod xn)

直接利用这个递推式,对f(x)f(x)f(x)求导,求逆,相乘,积分就可以得到g(x)g(x)g(x)

牛顿迭代:

给出函数f(x)f(x)f(x),求出他的零点

每次利用切线逼近,即令切线y=0y=0y=0

y=f′(x0)(x−x0)+f(x0)=0 y=f'(x_{0})(x-x_{0})+f(x_{0})=0 y=f(x0)(xx0)+f(x0)=0

x=x0−f(x0)f′(x0) x=x_{0}-\frac{f(x_{0})}{f'(x_{0})} x=x0f(x0)f(x0)

多项式牛顿迭代:

给出多项式f(x)f(x)f(x),求g(x)g(x)g(x),使得

f(g(x))≡0 (mod xn) f(g(x))\equiv 0\ (mod\ x^n) f(g(x))0 (mod xn)

即求多项式f(x)f(x)f(x)的一个零点,每次进行如下牛顿迭代

g(x)=g0(x)−f(g0(x))f′(g0(x)) g(x)=g_{0}(x)-\frac{f(g_{0}(x))}{f'(g_{0}(x))} g(x)=g0(x)f(g0(x))f(g0(x))

将分母求导展开,得到

g(x)=g0(x)−f(g0(x))f′(x)×g0′(x) g(x)=g_{0}(x)-\frac{f(g_{0}(x))}{f'(x)\times g_{0}'(x)} g(x)=g0(x)f(x)×g0(x)f(g0(x))

多项式expexpexp

要求一个g(x)g(x)g(x),使得

g(x)≡ef(x) (mod xn) g(x)\equiv e^{f(x)}\ (mod\ x^n) g(x)ef(x) (mod xn)

左右取对数得到

ln(g(x))≡f(x) (mod xn) ln(g(x))\equiv f(x)\ (mod\ x^n) ln(g(x))f(x) (mod xn)

移项有

ln(g(x))−f(x)≡0 (mod xn) ln(g(x))-f(x)\equiv 0\ (mod\ x^n) ln(g(x))f(x)0 (mod xn)

F(g(x))=ln(g(x))−f(x)F(g(x))=ln(g(x))-f(x)F(g(x))=ln(g(x))f(x),即求F(x)F(x)F(x)一个零点,牛顿迭代得到

g(x)=g0(x)−F(g0(x))F′(g0(x)) g(x)=g_{0}(x)-\frac{F(g_{0}(x))}{F'(g_{0}(x))} g(x)=g0(x)F(g0(x))F(g0(x))

g(x)=g0(x)−ln(g0(x))−f(x)1g0(x)=g0(x)(1−ln(g0(x))+f(x)) g(x)=g_{0}(x)-\frac{ln(g_{0}(x))-f(x)}{\frac{1}{g_{0}(x)}}=g_{0}(x)(1-ln(g_{0}(x))+f(x)) g(x)=g0(x)g0(x)1ln(g0(x))f(x)=g0(x)(1ln(g0(x))+f(x))

多项式快速幂:

求一个g(x)g(x)g(x),使得

g(x)≡fk(x) (mod xn) g(x)\equiv f^k(x)\ (mod\ x^n) g(x)fk(x) (mod xn)

利用x=elnxx=e^{lnx}x=elnx,有

g(x)≡eklnf(x) (mod xn) g(x)\equiv e^{klnf(x)}\ (mod\ x^n) g(x)eklnf(x) (mod xn)

进行一次ln+expln+expln+exp即可,幂可以直接取余

边界情况如果保证[x0]f(x)=0[x^0]f(x)=0[x0]f(x)=0,那么可以直接有[x0g(x)]=1[x^{0}g(x)]=1[x0g(x)]=1,否则需要欧拉定理取余

using ll=long long;
const int N=1<<18,inf=0x3fffffff;
const long long INF=0x3f3f3f3f3f3f,mod=998244353,inv2=499122177,inv3=332748118;

ll qpow(ll a,ll b)
{
	ll ret=1,base=a;
	while(b)
	{
		if(b&1) ret=ret*base%mod;
		base=base*base%mod;
		b>>=1;
	}
	return ret;
}

ll inv(ll x){return qpow(x,mod-2);}

namespace poly
{
	//多项式开根二次剩余部分
	ll si;
	struct Complex_MOD
	{
		ll a,b;
		Complex_MOD operator*(const Complex_MOD &t) const
		{
			Complex_MOD res;
			res.a=(a*t.a%mod+b*t.b%mod*si%mod)%mod;
			res.b=(a*t.b%mod+b*t.a%mod)%mod;
			return res;
		}
	};
	inline ll Pow_Complex(Complex_MOD val,ll b)
	{
		Complex_MOD res={1,0};
		while(b)
		{
			if(b&1) res=res*val;
			val=val*val;
			b>>=1;
		}
		return res.a;
	}
	ll squaremod(ll n)
	{
		if(!n) return 0;
		srand((unsigned)(time(NULL)));
		ll p1,p2;
		while(1)
		{
			p1=1ll*rand()*rand()%mod;
			p2=(p1*p1%mod-n+mod)%mod;
			if(::qpow(p2,(mod-1)/2)!=1) break;
		}
		si=p2;
		Complex_MOD val={p1,1};
		int ans=Pow_Complex(val,(mod+1)/2);
		if(mod-ans<ans) ans=mod-ans;
		return ans;
	}
	//以上是二次剩余
	int pos[N],invs[N],invcnt,sqrts[N],sqrtcnt,exps[N],expcnt;
	ll a[N],b[N],invtmp[N],dertmp[N],multitmp[N],lntmp[N],lnlntmp[N];
	int getlen(int k)
	{
		int ret=0;
		while(k){ret++;k>>=1;}
		return ret;
	}
	int getrev(int k,int len)
	{
		int ret=0;
		while(k){ret=(ret<<1|(k&1));k>>=1;len--;}
		return ret<<len;
	}
	int getpos(int n)
	{
		int limit=1;
		while(limit<=n) limit<<=1;
		int len=getlen(limit-1);
		for(int i=0;i<limit;i++) pos[i]=getrev(i,len);
		return limit;
	}
	void ntt(ll *a,int limit,int op)
	{
		for(int i=0;i<limit;i++)
			if(i<pos[i]) swap(a[i],a[pos[i]]);
		for(int len=2;len<=limit;len<<=1)
		{
			ll base=::qpow(op==1?3:inv3,(mod-1)/len);
			for(int l=0;l<limit;l+=len)
			{
				ll now=1;
				for(int i=l;i<l+len/2;i++)
				{
					ll x=a[i]%mod,y=now*a[i+len/2]%mod;
					a[i]=(x+y)%mod;
					a[i+len/2]=(x-y+mod)%mod;
					now=now*base%mod;
				}
			}
		}
	}
	void prepare(int *s,int &cnt,int n)
	{
		cnt=0;
		while(n>1) s[++cnt]=n,n=n+1>>1;
	}
	void inv(ll *f,ll *g,int n)//求出f的乘法逆元,放到g内,f的长度为n,需要保证g是空的
	{
		prepare(invs,invcnt,n); g[0]=::inv(f[0]);
		for(int i=invcnt;i>=1;i--)
		{
			int limit=getpos(invs[i]<<1);
			memcpy(a,f,sizeof(ll)*invs[i]); fill(a+invs[i],a+limit,0);
			ntt(a,limit,1); ntt(g,limit,1);
			for(int i=0;i<limit;i++) g[i]=g[i]*((2-a[i]*g[i]%mod)%mod+mod)%mod;
			ntt(g,limit,-1); ll tmp=::inv(limit);
			for(int i=0;i<limit;i++) g[i]=g[i]*tmp%mod;
			fill(g+invs[i],g+limit,0);
		}
	}
	void sqrt(ll *f,ll *g,int n)//多项式开根,存放在g内,需要保证g是空的
	{
		prepare(sqrts,sqrtcnt,n); g[0]=squaremod(f[0]);//二次剩余
		for(int i=sqrtcnt;i>=1;i--)
		{
			int limit=getpos(sqrts[i]<<1);
			memset(invtmp,0,sizeof(ll)*limit); inv(g,invtmp,sqrts[i]);
			memcpy(a,f,sizeof(ll)*sqrts[i]); fill(a+sqrts[i],a+limit,0);
			ntt(g,limit,1); ntt(invtmp,limit,1); ntt(a,limit,1);
			for(int i=0;i<limit;i++) g[i]=inv2*(g[i]+invtmp[i]*a[i]%mod)%mod;
			ntt(g,limit,-1); ll tmp=::inv(limit);
			for(int i=0;i<limit;i++) g[i]=g[i]*tmp%mod;
			fill(g+sqrts[i],g+limit,0);
		}
	}
	void derivation(ll *f,ll *g,int n)//f求导,放入g
	{
		for(int i=0;i<n-1;i++) g[i]=f[i+1]*(i+1);
		g[n-1]=0;
	}
	void integral(ll *f,ll *g,int n)//f积分,放入g
	{
		g[0]=0;
		for(int i=1;i<n;i++) g[i]=f[i-1]*::inv(i)%mod;
	}
	void multi(ll *f,ll *g,ll *t,int n,int m)//f*g放入t
	{
		int limit=getpos(n+m);
		memcpy(a,f,sizeof(ll)*n); fill(a+n,a+limit,0);
		memcpy(t,g,sizeof(ll)*m); fill(t+m,t+limit,0);
		ntt(a,limit,1); ntt(t,limit,1);
		for(int i=0;i<limit;i++) t[i]=t[i]*a[i]%mod;
		ntt(t,limit,-1); ll tmp=::inv(limit);
		for(int i=0;i<limit;i++) t[i]=t[i]*tmp%mod;
		fill(t+n+m,t+limit,0);
	}
	void ln(ll *f,ll *g,int n)
	{
		derivation(f,dertmp,n);
		int limit=1;
		while(limit<=(n<<1)) limit<<=1;
		memset(invtmp,0,sizeof(ll)*limit);
		inv(f,invtmp,n);
		multi(dertmp,invtmp,multitmp,n,n);
		integral(multitmp,g,n);
	}
	void exp(ll *f,ll *g,int n)//e^(f)放入g,需要保证g是空的
	{
		prepare(exps,expcnt,n); g[0]=1;
		for(int i=expcnt;i>=1;i--)
		{
			int limit=getpos(exps[i]<<1);
			ln(g,lntmp,exps[i]); fill(lntmp+exps[i],lntmp+limit,0);
			memcpy(a,f,sizeof(ll)*exps[i]); fill(a+exps[i],a+limit,0);
			ntt(g,limit,1); ntt(lntmp,limit,1); ntt(a,limit,1);
			for(int i=0;i<limit;i++) g[i]=g[i]*(((1-lntmp[i]+a[i])%mod+mod)%mod)%mod;
			ntt(g,limit,-1); ll tmp=::inv(limit);
			for(int i=0;i<limit;i++) g[i]=g[i]*tmp%mod;
			fill(g+exps[i],g+limit,0);
			memset(lntmp,0,sizeof(ll)*limit);
		}
	}
	void read(char *s,int n,ll &k1,ll &k2,ll &k3)
	{
		k1=k2=k3=0; int len=strlen(s+1);
		for(int i=1;i<=len;i++)
		{
			k1=(10*k1+(s[i]-'0'))%mod; 
			k2=(10*k2+(s[i]-'0'))%(mod-1);
			if(k3<n) k3=10*k3+(s[i]-'0'); 
		}
	}
	void qpow(ll *f,ll *g,char *s,int n)//字符串形式给出幂,f^(k)放入g中,需要保证g是空的
	{
		ll k1,k2,k3; int sta=0;
		read(s,n,k1,k2,k3);
		while(sta<n&&f[sta]==0) sta++;
		if(sta==n||sta*k1>n||(f[0]==0&&k3>=n))
		{
			for(int i=0;i<n;i++) g[i]=0;
			return;
		}
		ll invsta=::inv(f[sta]),tmp=::qpow(f[sta],k2);
		for(int i=0;i<n;i++) f[i]=f[i+sta]*invsta%mod;
		ln(f,lnlntmp,n);
		for(int i=0;i<n;i++) lnlntmp[i]=lnlntmp[i]*k1%mod;
		exp(lnlntmp,g,n);
		for(int i=n-1;i>=sta*k1;i--) g[i]=g[i-sta*k1]*tmp%mod;
		for(int i=0;i<sta*k1;i++) g[i]=0;
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值