多项式的各种操作

前置知识:多项式乘法FFT/NTT

多项式求逆

F ( x ) G ( x ) ≡ 1 ( m o d   x n ) F(x)G(x) ≡ 1( mod \ x^n) F(x)G(x)1(mod xn)

已知 F ( x ) F(x) F(x) G ( x ) G(x) G(x)

解法:

假设已经求出了 F ( x ) G ′ ( x ) ≡ 1 ( m o d x ⌈ n 2 ⌉ ) F(x)G'(x) ≡ 1( mod x^{\lceil {\frac{n}{2}} \rceil}) F(x)G(x)1(modx2n)
显然
F ( x ) G ( x ) ≡ 1 ( m o d   x ⌈ n 2 ⌉ ) F(x)G(x) ≡ 1( mod \ x^{\lceil {\frac{n}{2}} \rceil}) F(x)G(x)1(mod x2n)

拿下面那个减一下上面那个

F ( x ) ( G ( x ) − G ′ ( x ) ) ≡ 0 ( m o d   x ⌈ n 2 ⌉ ) F(x)(G(x) - G'(x)) ≡ 0( mod \ x^{\lceil {\frac{n}{2}} \rceil}) F(x)(G(x)G(x))0(mod x2n)

因为 F ( x ) 不 同 余 0 ( m o d   x ⌈ n 2 ⌉ ) F(x) 不同余 0( mod \ x^{\lceil {\frac{n}{2}} \rceil}) F(x)0(mod x2n)
所以

G ( x ) − G ′ ( x ) ≡ 0 ( m o d   x ⌈ n 2 ⌉ ) G(x) - G'(x) ≡ 0( mod \ x^{\lceil {\frac{n}{2}} \rceil}) G(x)G(x)0(mod x2n)
然后很naive地把它左右平方

G ( x ) 2 + G ′ ( x ) 2 − 2 G ( x ) G ′ ( x ) ≡ 0 ( m o d   x n ) G(x) ^ 2 + G'(x) ^ 2 - 2 G(x)G'(x)≡ 0( mod \ x^n) G(x)2+G(x)22G(x)G(x)0(mod xn)

再乘 F ( x ) F(x) F(x)

G ( x ) + G ′ ( x ) 2 F ( x ) − 2 G ′ ( x ) ≡ 0 ( m o d   x n ) G(x) + G'(x) ^ 2F(x) - 2G'(x)≡ 0( mod \ x^n) G(x)+G(x)2F(x)2G(x)0(mod xn)

移项
G ( x ) ≡ 2 G ′ ( x ) − G ′ ( x ) 2 F ( x ) ( m o d   x n ) G(x) ≡ 2G'(x) - G'(x) ^ 2F(x) ( mod \ x^n) G(x)2G(x)G(x)2F(x)(mod xn)

然后就直接做就好了

根据主定理这个东东的之间复杂度是 T ( n ) = T ( n / 2 ) + n l o g n = n l o g n T(n) = T(n/2) + n log n =n log n T(n)=T(n/2)+nlogn=nlogn

code:

#include<bits/stdc++.h>
#define int long long
#define mod 998244353	
#define G 3
#define N 8000005
using namespace std;
int qpow(int x, int y){
	int ret = 1;
	for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
	return ret;
}
int rev[N], G_inv, len_inv;
void ntt(int *a, int len, int o){//NTT板子
	len_inv = qpow(len, mod - 2);
	for(int i = 0; i <= len; i ++) rev[i] = (rev[i >> 1] >> 1) | ((i&1) * len >> 1);
	for(int i = 0; i <= len; i ++) if(i < rev[i]) swap(a[i], a[rev[i]]);
	for(int i = 2; i <= len; i <<= 1){
		int wn = qpow((o == 1)? G:G_inv, (mod - 1) / i);
		for(int j = 0, p = i / 2; j + i - 1 <= len; j += i){
			int w0 = 1;
			for(int k = j; k < j + p; k ++, w0 = w0 * wn % mod){
				int X = a[k], Y = w0 * a[k + p] % mod;
				a[k] = (X + Y) % mod;
				a[k + p] = (X - Y + mod) % mod;
			}
		}
	}
	if(o == -1) 
		for(int i = 0; i <= len; i ++) a[i] = a[i] * len_inv % mod;
}
int c[N];
void inv(int *a, int *b, int sz){
	if(sz == 1) {b[0] = qpow(a[0], mod - 2); return;}//常数直接求逆元
	inv(a, b, (sz + 1) / 2);//把小的先求出来
	int len = 1;
	for(; len <= sz + sz; len <<= 1);
	for(int i = 0; i < sz; i ++) c[i] = a[i];//注意要用c替换一下,不然a会做很多次NTT
	for(int i = sz; i <= len; i ++) c[i] = 0;
	ntt(c, len, 1), ntt(b, len, 1);
	for(int i = 0; i <= len; i ++) b[i] = (b[i] * 2 % mod - b[i] * b[i] % mod * c[i] % mod + mod) % mod;//直接算
	ntt(b, len, -1);
	for(int i = sz; i <= len; i ++) b[i] = 0;//注意,b要用很多次,所以要把后面的清空一下
}
int a[N], b[N], n, m;
signed main(){
	G_inv = qpow(G, mod - 2);
	scanf("%lld", &n);
	for(int i = 0; i < n; i ++) scanf("%lld", &a[i]);
	inv(a, b, n);
	for(int i = 0; i < n; i ++) printf("%lld ", b[i]);
	return 0;
}

多项式除法

F ( x ) 最 高 次 是 n , G ( x ) 最 高 次 是 m , 则 Q ( x ) 最 高 次 是 n − m , R 最 高 次 是 m − 1 F(x)最高次是n, G(x)最高次是m, 则Q(x)最高次是n-m, R最高次是m-1 F(x)n,G(x)m,Q(x)nm,Rm1
F ( x ) = Q ( x ) G ( x ) + R ( x ) F(x)=Q(x)G(x)+R(x) F(x)=Q(x)G(x)+R(x)

1 x \frac{1}{x} x1代入

F ( 1 x ) = Q ( 1 x ) G ( 1 x ) + R ( 1 x ) F(\frac{1}{x})=Q(\frac{1}{x})G(\frac{1}{x})+R(\frac{1}{x}) F(x1)=Q(x1)G(x1)+R(x1)

x n F ( 1 x ) = x n Q ( 1 x ) G ( 1 x ) + x n R ( 1 x ) x^nF(\frac{1}{x})=x^nQ(\frac{1}{x})G(\frac{1}{x})+x^nR(\frac{1}{x}) xnF(x1)=xnQ(x1)G(x1)+xnR(x1)

然后发现 x n F ( 1 x ) = F R ( x )     即 将 F 的 系 数 前 后 翻 转 一 下 x^nF(\frac{1}{x}) = F_R(x)\ \ \ 即将F的系数前后翻转一下 xnF(x1)=FR(x)   F

x n F ( 1 x ) = x n − m Q ( 1 x ) x m G ( 1 x ) + x n − m + 1 x m − 1 R ( 1 x ) x^nF(\frac{1}{x})=x^{n - m}Q(\frac{1}{x})x^mG(\frac{1}{x})+x^{n-m+1}x^{m-1}R(\frac{1}{x}) xnF(x1)=xnmQ(x1)xmG(x1)+xnm+1xm1R(x1)

F R ( x ) = Q R ( x ) G R ( x ) + x n − m + 1 R R ( x ) F_R(x)=Q_R(x)G_R(x)+x^{n-m+1}R_R(x) FR(x)=QR(x)GR(x)+xnm+1RR(x)

两边   m o d       x n − m + 1 \bmod \ \ \ x^{n-m+1} mod   xnm+1
F R ( x ) = Q R ( x ) G R ( x ) ( m o d     x n − m + 1 ) F_R(x)=Q_R(x)G_R(x)(mod \ \ \ x^{n-m+1}) FR(x)=QR(x)GR(x)(mod   xnm+1)
因为 Q ( x ) 的 最 高 次 是 n − m < n − m + 1 Q(x) 的最高次是 n-m < n-m+1 Q(x)nm<nm+1所以没关系(但注意要补齐)

然后变一下形
Q R ( x ) = F R ( x ) G R − 1 ( x ) ( m o d     x n − m + 1 ) Q_R(x)=F_R(x)G_R^{-1}(x)(mod \ \ \ x^{n-m+1}) QR(x)=FR(x)GR1(x)(mod   xnm+1)

G R ( x ) 的 逆 元 即 可 G_R(x)的逆元即可 GR(x)

R ( x ) = F ( x ) − G ( x ) Q ( x ) R(x) = F(x) - G(x)Q(x) R(x)=F(x)G(x)Q(x)


#include<bits/stdc++.h>
#define int long long
#define mod 998244353	
#define GG 3
#define N 8000005
using namespace std;
int qpow(int x, int y){
	int ret = 1;
	for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
	return ret;
}
int rev[N], GG_inv, len_inv;
void ntt(int *a, int len, int o){//NTT板子
	len_inv = qpow(len, mod - 2);
	for(int i = 0; i <= len; i ++) rev[i] = (rev[i >> 1] >> 1) | ((i&1) * len >> 1);
	for(int i = 0; i <= len; i ++) if(i < rev[i]) swap(a[i], a[rev[i]]);
	for(int i = 2; i <= len; i <<= 1){
		int wn = qpow((o == 1)? GG:GG_inv, (mod - 1) / i);
		for(int j = 0, p = i / 2; j + i - 1<= len; j += i){
			int w0 = 1;
			for(int k = j; k < j + p; k ++, w0 = w0 * wn % mod){
				int X = a[k], Y = w0 * a[k + p] % mod;
				a[k] = (X + Y) % mod;
				a[k + p] = (X - Y + mod) % mod;
			}
		}
	}
	if(o == -1) 
		for(int i = 0; i <= len; i ++) a[i] = a[i] * len_inv % mod;
}
int c[N];
void inv(int *a, int *b, int sz){//多项式求逆板子
	if(sz == 0) {b[0] = qpow(a[0], mod - 2); return;}
	inv(a, b, sz / 2);
	int len = 1;
	for(; len <= sz + sz; len <<= 1);
	for(int i = 0; i <= sz; i ++) c[i] = a[i];
	for(int i = sz + 1; i <= len; i ++) c[i] = 0;
	ntt(c, len, 1), ntt(b, len, 1);
	for(int i = 0; i <= len; i ++) b[i] = (b[i] * 2 % mod - b[i] * b[i] % mod * c[i] % mod + mod) % mod;
	ntt(b, len, -1);
	for(int i = sz + 1; i <= len; i ++) b[i] = 0;
}
void mul(int *a, int *b, int n, int m){//多项式乘法板子
	int len = 1;
	for(; len <= n + m; len <<= 1);
	ntt(a, len, 1), ntt(b, len, 1);
	for(int i = 0; i <= len; i ++) a[i] = a[i] * b[i] % mod;
	ntt(a, len, -1);
}
int a[N], b[N], F[N], G[N], Q[N], GR[N], FR[N], GR_inv[N], R[N], n, m;
signed main(){
	GG_inv = qpow(GG, mod - 2);
	scanf("%lld%lld", &n, &m);
	for(int i = 0; i <= n; i ++) scanf("%lld", &F[i]), FR[n - i] = F[i];
	for(int i = 0; i <= m; i ++) scanf("%lld", &G[i]), GR[m - i] = G[i];
	for(int i = n - m + 1; i <= m; i ++) GR[i] = 0;// mod n - m + 1, 即把后面的全部清0
	inv(GR, GR_inv, n - m);//注意长度是 n - m, 要补齐
	mul(FR, GR_inv, n, n - m);	//长度要补齐
	for(int i = 0; i <= n - m; i ++) Q[i] = FR[n - m - i];//翻转
	for(int i = 0; i <=  n - m; i ++) printf("%lld ", Q[i]); printf("\n");//输出
	mul(G, Q, m, n - m);//直接算余数R
	for(int i = 0; i < m; i ++) R[i] = (F[i] - G[i] + mod) % mod;
	for(int i = 0; i <  m; i ++) printf("%lld ", R[i]); printf("\n");
	return 0;
}
posted @ 2019-10-03 14:09  lahlah  阅读(33)  评论(0编辑  收藏  举报