【luogu P4721】【模板】分治 FFT(NTT)(多项式求逆 / cdq分治)

【模板】分治 FFT

题目链接:luogu P4721

题目大意

给你多项式 G G G 满足 G 0 = 0 G_0=0 G0=0
然后要你求多项式 F F F 的前 n n n 项满足: F i = ∑ j = 1 i F i − j G j F_{i}=\sum\limits_{j=1}^iF_{i-j}G_j Fi=j=1iFijGj

思路

cdq分治

你会发现对于一个你要求的范围 [ l , r ] [l,r] [l,r],你如果求出了 [ l , m i d ] [l,mid] [l,mid],你就可以拿他来求它这个部分对 [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 的贡献,然后问题就变成了求 [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 的。
所以就是一个 cdq 分治。

那你考虑这个横跨的部分怎么求。
那你 F F F [ l , m i d ] [l,mid] [l,mid],那你 G G G 就应该给 [ 0 , r − l + 1 ] [0,r-l+1] [0,rl+1]
然后我们就做卷积即可算出贡献。
(我们可以把 F F F 左移 l l l 位,卷出来再右移回去)

多项式求逆

推式子:
F i = ∑ i = 0 ∞ f i x i F_i=\sum\limits_{i=0}^{\infty}f_ix^i Fi=i=0fixi
G i = ∑ i = 0 ∞ g i x i G_i=\sum\limits_{i=0}^{\infty}g_ix^i Gi=i=0gixi
F G = ∑ i = 0 ∞ f i x i ∑ i = 0 ∞ g i x i = ∑ i = 0 ∞ ( ∑ j = 0 i f n − j g j ) x i FG=\sum\limits_{i=0}^{\infty}f_ix^i\sum\limits_{i=0}^{\infty}g_ix^i=\sum\limits_{i=0}^{\infty}(\sum\limits_{j=0}^{i}f_{n-j}g_{j})x^i FG=i=0fixii=0gixi=i=0(j=0ifnjgj)xi
F = ∑ i = 0 ∞ ( ∑ j = 1 i f i − j g j ) x i F=\sum\limits_{i=0}^{\infty}(\sum\limits_{j=1}^if_{i-j}g_j)x^i F=i=0(j=1ifijgj)xi
然后由于 g 0 = 0 g_0=0 g0=0,所以 F F F 少的 f i g 0 f_ig_0 fig0 其实是没有关系的。
但你会发现其实 F ≠ F G F\neq FG F=FG,那问题在哪里呢,你看一下会发现 F G FG FG 的常数项是 0 0 0
所以应该是: F − f 0 = F G F-f_0=FG Ff0=FG
F ( 1 − G ) = f 0 F(1-G)=f_0 F(1G)=f0
F = f 0 1 − G F=\dfrac{f_0}{1-G} F=1Gf0
然后就可以用多项式求逆得到啦。

代码

cdq分治

#include<cstdio>
#include<cstring>
#include<algorithm>
#define mo 998244353
#define clr(f, n) memset(f, 0, (n) * sizeof(int))
#define cpy(f, g, n) memcpy(f, g, (n) * sizeof(int))

using namespace std;

const int N = 100000 * 8 + 1;
int n, m, f[N], g[N], an[N], inv[N], G, Gv;

int jia(int x, int y) {return x + y >= mo ? x + y - mo : x + y;}
int jian(int x, int y) {return x - y < 0 ? x - y + mo : x - y;}
int cheng(int x, int y) {return 1ll * x * y % mo;}
int ksm(int x, int y) {int re = 1; while (y) {if (y & 1) re = cheng(re, x); x = cheng(x, x); y >>= 1;} return re;}

void Init() {
	G = 3; Gv = ksm(G, mo - 2);
	inv[0] = inv[1] = 1; for (int i = 2; i < N; i++) inv[i] = cheng(inv[mo % i], mo - mo / i);
}

void get_an(int limit, int l_size) {
	for (int i = 0; i < limit; i++)
		an[i] = (an[i >> 1] >> 1) | ((i & 1) << (l_size - 1)); 
}

void NTT(int *f, int limit, int op) {
	for (int i = 0; i < limit; i++) if (an[i] < i) swap(f[an[i]], f[i]);
	for (int mid = 1; mid < limit; mid <<= 1) {
		int Wn = ksm(op == 1 ? G : Gv, (mo - 1) / (mid << 1));
		for (int R = (mid << 1), j = 0; j < limit; j += R) {
			int w = 1;
			for (int k = 0; k < mid; k++, w = cheng(w, Wn)) {
				int x = f[j | k], y = cheng(w, f[j | mid | k]);
				f[j | k] = jia(x, y); f[j | mid | k] = jian(x, y);
			}
		}
	}
	if (op == -1) {
		int limv = ksm(limit, mo - 2);
		for (int i = 0; i < limit; i++) f[i] = cheng(f[i], limv);
	}
}

void px(int *f, int *g, int limit) {
	for (int i = 0; i < limit; i++)
		f[i] = cheng(f[i], g[i]);
}

int A[N], B[N];

void slove(int *f, int *g, int l, int r) {
	if (l == r) return ;
	int mid = (l + r) >> 1, sz = r - l + 1;
	slove(f, g, l, mid);
	
	cpy(A, f + l, mid - l + 1); cpy(B, g, sz);
	int limit = 1, l_size = 0; while (limit <= sz) limit <<= 1, l_size++;
	get_an(limit, l_size);
	clr(A + mid - l + 1, limit - (mid - l + 1)); clr(B + sz, limit - sz);
	NTT(A, limit, 1); NTT(B, limit, 1); px(A, B, limit); NTT(A, limit, -1);
	for (int i = mid + 1; i <= r; i++)
		f[i] = jia(f[i], A[i - l]);
	
	slove(f, g, mid + 1, r);
}

int main() {
	Init();
	
	scanf("%d", &n);
	for (int i = 1; i < n; i++) scanf("%d", &g[i]);
	
	f[0] = 1; slove(f, g, 0, n - 1);
	
	for (int i = 0; i < n; i++) printf("%d ", f[i]);
	
	return 0;
}

多项式求逆

#include<cstdio>
#include<cstring>
#include<algorithm>
#define mo 998244353
#define clr(f, n) memset(f, 0, (n) * sizeof(int))
#define cpy(f, g, n) memcpy(f, g, (n) * sizeof(int))

using namespace std;

const int N = 100000 * 8 + 1;
int n, f[N], g[N], an[N], inv[N], G, Gv;

int jia(int x, int y) {return x + y >= mo ? x + y - mo : x + y;}
int jian(int x, int y) {return x - y < 0 ? x - y + mo : x - y;}
int cheng(int x, int y) {return 1ll * x * y % mo;}
int ksm(int x, int y) {int re = 1; while (y) {if (y & 1) re = cheng(re, x); x = cheng(x, x); y >>= 1;} return re;}

void Init() {
	G = 3; Gv = ksm(G, mo - 2);
	inv[0] = inv[1] = 1; for (int i = 2; i < N; i++) inv[i] = cheng(inv[mo % i], mo - mo / i);
}

void get_an(int limit, int l_size) {
	for (int i = 0; i < limit; i++)
		an[i] = (an[i >> 1] >> 1) | ((i & 1) << (l_size - 1)); 
}

void NTT(int *f, int limit, int op) {
	for (int i = 0; i < limit; i++) if (an[i] < i) swap(f[an[i]], f[i]);
	for (int mid = 1; mid < limit; mid <<= 1) {
		int Wn = ksm(op == 1 ? G : Gv, (mo - 1) / (mid << 1));
		for (int R = (mid << 1), j = 0; j < limit; j += R) {
			int w = 1;
			for (int k = 0; k < mid; k++, w = cheng(w, Wn)) {
				int x = f[j | k], y = cheng(w, f[j | mid | k]);
				f[j | k] = jia(x, y); f[j | mid | k] = jian(x, y);
			}
		}
	}
	if (op == -1) {
		int limv = ksm(limit, mo - 2);
		for (int i = 0; i < limit; i++) f[i] = cheng(f[i], limv);
	}
}

void px(int *f, int *g, int limit) {
	for (int i = 0; i < limit; i++)
		f[i] = cheng(f[i], g[i]);
}

void times(int *f, int *g, int n, int m) {
	static int tmp[N];
	int limit = 1, l_size = 0; while (limit < n + n) limit <<= 1, l_size++;
	cpy(tmp, g, n); clr(tmp + n, limit - n);
	get_an(limit, l_size);
	NTT(f, limit, 1); NTT(tmp, limit, 1);
	px(f, tmp, limit); NTT(f, limit, -1);
	clr(f + m, limit - m); clr(tmp, limit);
}

void invp(int *f, int n) {
	static int w[N], r[N], tmp[N];
	w[0] = ksm(f[0], mo - 2);
	int limit = 1, l_size = 0;
	for (int len = 2; (len >> 1) <= n; len <<= 1) {
		limit = len; l_size++; get_an(limit, l_size);
		cpy(r, w, len >> 1);
		cpy(tmp, f, limit); NTT(tmp, limit, 1);
		NTT(r, limit, 1); px(r, tmp, limit);
		NTT(r, limit, -1); clr(r, limit >> 1);
		cpy(tmp, w, len); NTT(tmp, limit, 1);
		NTT(r, limit, 1); px(r, tmp, limit);
		NTT(r, limit, -1);
		for (int i = (len >> 1); i < len; i++)
			w[i] = jian(cheng(w[i], 2), r[i]);
	}
	cpy(f, w, n); clr(w, limit); clr(r, limit); clr(tmp, limit);
}

void dao(int *f, int n) {
	for (int i = 1; i < n; i++)
		f[i - 1] = cheng(f[i], i);
	f[n - 1] = 0;
}

void jifen(int *f, int n) {
	for (int i = n; i >= 1; i--)
		f[i] = cheng(f[i - 1], inv[i]);
	f[0] = 0;
}

void mof(int *f, int n, int *g, int m) {
	static int f_[N], g_[N];
	int L = n - m + 1;
	reverse(f, f + n); cpy(f_, f, L); reverse(f, f + n); 
	reverse(g, g + m); cpy(g_ , g, L); reverse(g, g + m);
	invp(g_, L); times(g_, f_, L, L); reverse(g_, g_ + L);
	
	times(g, g_, n, n);
	for (int i = 0; i < m - 1; i++) g[i] = jian(f[i], g[i]);
	clr(g + m - 1, L);
	cpy(f, g_, L); clr(f + L, n - L);
}

void lnp(int *f, int n) {
	static int g[N];
	cpy(g, f, n); dao(g, n);
	invp(f, n); times(f, g, n, n);
	jifen(f, n - 1); clr(g, n);
}

void exp(int *f, int n) {
	static int w[N], ww[N];
	ww[0] = 1;
	int len;
	for (len = 2; (len >> 1) <= n; len <<= 1) {
		cpy(w, ww, len >> 1); lnp(w, len);
		for (int i = 0; i < len; i++)
			w[i] = jian(f[i], w[i]);
		w[0] = jia(w[0], 1);
		times(ww, w, len, len);
	}
	len >>= 1;
	cpy(f, ww, n); clr(ww, len); clr(w, len);
}

void ksmp(int *f, int n, int k) {
	lnp(f, n);
	for (int i = 0; i < n; i++) f[i] = cheng(f[i], k);
	exp(f, n);
}

int main() {
	Init();
	
	scanf("%d", &n);
	for (int i = 1; i < n; i++) scanf("%d", &f[i]); int f0 = 1;
	
	for (int i = 0; i <= n; i++) f[i] = jian((i == 0), f[i]);
	invp(f, n);
	for (int i = 1; i <= n; i++) f[i] = cheng(f0, f[i]);
	
	for (int i = 0; i < n; i++) printf("%d ", f[i]);
	
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值