P5205 【模板】多项式开根

本文详细介绍了如何解决给定n-1次多项式A(x),求其平方根B(x)模x^n的问题。利用快速数论变换(NTT)和多项式乘法逆,首先找到H(x),满足H^2(x)≡A(x)modx^⌈n/2⌉,然后通过方程求解得到B(x)。算法的时间复杂度为O(nlog^2n)。代码示例展示了具体的实现过程。

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

P5205 【模板】多项式开根

题目大意

给你一个 n − 1 n-1 n1的多项式 A ( x ) A(x) A(x),求一个在   m o d   x n \bmod x^n modxn意义下的多项式 B ( x ) B(x) B(x),使得 B 2 ( x ) ≡ A ( x ) ( m o d x n ) B^2(x)\equiv A(x)\pmod{x^n} B2(x)A(x)(modxn)。若有多解,请取零次项系数较小的作为答案。

多项式系数在模 998244353 998244353 998244353的意义下进行计算。


题解

前置知识:

设已经求出了 H ( x ) H(x) H(x),使其满足

H 2 ( x ) ≡ A ( x ) ( m o d x ⌈ n 2 ⌉ ) H^2(x)\equiv A(x)\pmod{x^{\lceil\frac n2\rceil}} H2(x)A(x)(modx2n)

那么有

B ( x ) − H ( x ) ≡ 0 ( m o d x ⌈ n 2 ⌉ ) B(x)-H(x)\equiv 0\pmod{x^{\lceil\frac n2\rceil}} B(x)H(x)0(modx2n)

平方得

B 2 ( x ) − 2 B ( x ) ⋅ H ( x ) + H 2 ( x ) ≡ 0 ( m o d x n ) B^2(x)-2B(x)\cdot H(x)+H^2(x)\equiv 0\pmod{x^n} B2(x)2B(x)H(x)+H2(x)0(modxn)

A ( x ) A(x) A(x)替换 B 2 ( x ) B^2(x) B2(x)

B ( x ) = A ( x ) + H 2 ( x ) 2 H ( x ) B(x)=\dfrac{A(x)+H^2(x)}{2H(x)} B(x)=2H(x)A(x)+H2(x)

用多项式求逆和 N T T NTT NTT即可。

注意求 B ( x ) B(x) B(x)时可以先求 v = A ( x ) H ( x ) v=\dfrac{A(x)}{H(x)} v=H(x)A(x),然后 B ( x ) = v + H ( x ) 2 B(x)=\dfrac{v+H(x)}{2} B(x)=2v+H(x)

时间复杂度为 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)

code

#include<bits/stdc++.h>
using namespace std;
long long w,wn,f[500005],g[500005],a1[500005],a2[500005],a3[500005],v[500005];
const long long G=3,mod=998244353,ny2=499122177;
long long mi(long long t,long long v){
	if(!v) return 1;
	long long re=mi(t,v/2);
	re=re*re%mod;
	if(v&1) re=re*t%mod;
	return re;
}
void ch(long long *a,int l){
	for(int i=1,j=l/2;i<l-1;i++){
		if(i<j) swap(a[i],a[j]);
		int k=l/2;
		while(j>=k){
			j-=k;k>>=1;
		}
		j+=k;
	}
}
void ntt(long long *a,int l,int fl){
	for(int i=2;i<=l;i<<=1){
		if(fl==1) wn=mi(G,(mod-1)/i);
		else wn=mi(G,mod-1-(mod-1)/i);
		for(int j=0;j<l;j+=i){
			w=1;
			for(int k=j;k<j+i/2;k++,w=w*wn%mod){
				long long t=a[k],u=w*a[k+i/2]%mod;
				a[k]=(t+u)%mod;
				a[k+i/2]=(t-u+mod)%mod;
			}
		}
	}
	if(fl==-1){
		long long ny=mi(l,mod-2);
		for(int i=0;i<l;i++) a[i]=a[i]*ny%mod;
	}
}
void gt(int l){
	if(l==1){
		a2[0]=mi(g[0],mod-2);
		return;
	}
	gt((l+1)/2);
	int len=1;
	while(len<2*l) len<<=1;
	for(int i=0;i<l;i++) v[i]=g[i];
	for(int i=l;i<len;i++) v[i]=0;
	ch(v,len);ch(a2,len);
	ntt(v,len,1);ntt(a2,len,1);
	for(int i=0;i<len;i++){
		a2[i]=(2-v[i]*a2[i]%mod+mod)%mod*a2[i]%mod;
	}
	ch(a2,len);
	ntt(a2,len,-1);
	for(int i=l;i<len;i++) a2[i]=0;
}
void pdg(int l){
	for(int i=0;i<l;i++) a3[i]=g[i];
	ch(a3,l);
	ntt(a3,l,1);
	for(int i=0;i<l;i++) a3[i]=a3[i]*a3[i]%mod;
	ch(a3,l);
	ntt(a3,l,-1);
}
void solve(int l){
	if(l==1){
		g[0]=1;
		return;
	}
	solve((l+1)/2);
	int len=1;
	while(len<2*l) len<<=1;
	for(int i=0;i<l;i++) a1[i]=f[i];
	for(int i=l;i<len;i++) a1[i]=0;
	gt(len/2);
	ch(a1,len);ch(a2,len);
	ntt(a1,len,1);ntt(a2,len,1);
	for(int i=0;i<len;i++) a1[i]=a1[i]*a2[i]%mod;
	ch(a1,len);
	ntt(a1,len,-1);
	for(int i=0;i<len;i++){
		g[i]=(a1[i]+g[i])%mod*ny2%mod;
		a1[i]=a2[i]=0;
	}
	for(int i=l;i<len;i++) g[i]=0;
	pdg(len);
}
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%lld",&f[i]);
	}
	solve(n);
	for(int i=0;i<n;i++){
		printf("%lld ",g[i]);
	}
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值