bzoj1919: [Ctsc2010]性能优化

bzoj1919: [Ctsc2010]性能优化

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
权限题贴个题面。
luogu上也有。

分析

循环卷积裸题。
由于 w n i = w n i m o d    n w_n^i= w_n^{i \mod n} wni=wnimodn
所以说 w n i w n j = w n ( i + j ) m o d    n w_n^iw_n^j=w_n^{(i+j)\mod n} wniwnj=wn(i+j)modn
发现这刚好是循环卷积的形式。
所以说做长度为 n n n D F T DFT DFT之后再 I D F T IDFT IDFT就好了。
重点在于这题的 n n n的长度不是 2 n 2^n 2n,模数也非常诡异。
所以要介绍Cooley–Tukey FFT algorithm
注意到模数比较小而且是质数,所以直接映射单位复根到模意义下的原根。
回忆快速数论变换的过程。
e 2 π i n ≡ g P − 1 n e^{\frac{2\pi i}{n}}\equiv g^{\frac{P-1}{n}} en2πignP1
这个时候我们不这么搞,直接
e 2 π i n ≡ g e^{\frac{2\pi i}{n}}\equiv g en2πig
带点值的时候直接把 g k g^k gk带进去就好了。
然后最关键的问题来了, n n n不是 2 n 2^n 2n
但是题目给了一个条件 n = 2 k 1 3 k 2 5 k 3 7 k 4 n=2^{k_1}3^{k_2}5^{k_3}7^{k_4} n=2k13k25k37k4
F F T FFT FFT原本是即将序列分成两半,然后合并。
现在就吧序列分成 k = 2 , 3 , 5 , 7 k=2,3,5,7 k=2,3,5,7块,然后合并
比较麻烦的事情就来了——式子得重推。
对于一次分治,假设当前是分成 p p p块,当前总长度为 L L L,那么分治之后每块的大小就是 d = L p d=\frac{L}{p} d=pL
A ( g k ) = a 0 + a 1 g k + a 2 g 2 k ⋯ a L − 1 g k ( L − 1 ) A(g^k)=a_0+a_1g^k+a_2g^{2k}\cdots a_{L-1}g^{k(L-1)} A(gk)=a0+a1gk+a2g2kaL1gk(L1)
为了方便,接下来的式子中令 X = g k X=g^k X=gk
按照 m o d    p \mod p modp的剩余系分类
= ( a 0 + a p X p + ⋯ a p ( d − 1 ) X p ( d − 1 ) ) ) + ( a 1 X 1 + a p + 1 X p + 1 ⋯ a p ( d − 1 ) + 1 X p ( d − 1 ) + 1 ) + ⋯ + ( a p − 1 X p − 1 + a p − 1 + d X p − 1 + d ⋯ a p d − 1 X p d − 1 ) =(a_0+a_pX^p+\cdots a_{p(d-1)}X^{p(d-1))})+(a_1X^1+a_{p+1}X^{p+1}\cdots a_{p(d-1)+1}X^{p(d-1)+1})+\cdots +(a_{p-1}X^{p-1}+a_{p-1+d}X^{p-1+d}\cdots a_{pd-1}X^{pd-1}) =(a0+apXp+ap(d1)Xp(d1)))+(a1X1+ap+1Xp+1ap(d1)+1Xp(d1)+1)++(ap1Xp1+ap1+dXp1+dapd1Xpd1)
= ∑ 0 ≤ b &lt; p ∑ 0 ≤ a &lt; d a a p + b X a p + b = ∑ 0 ≤ b &lt; p X b ∑ 0 ≤ a &lt; d a a p + b X a p =\sum_{0\le b&lt; p}\sum_{0\le a&lt;d}a_{ap+b}X^{ap+b}=\sum_{0\le b&lt; p}X^b\sum_{0\le a&lt;d}a_{ap+b}X^{ap} =0b<p0a<daap+bXap+b=0b<pXb0a<daap+bXap
考虑分治往下一层,这个时候稍微注意一下下一层的点值用 w = g p w=g^p w=gp的次幂去带。
如果我们已经得到了分治之后的结果,也就是
A 0 ( w m ) = a 0 + a p w m + ⋯ a p ( d − 1 ) w m ( d − 1 ) = ∑ 0 ≤ a &lt; d a a p w m a A_0(w^m)=a_0+a_pw^m+\cdots a_{p(d-1)}w^{m(d-1)}=\sum_{0\le a &lt; d}a_{ap}w^{ma} A0(wm)=a0+apwm+ap(d1)wm(d1)=0a<daapwma
A b ( w m ) = ∑ 0 ≤ a &lt; d a a p + b w m a A_b(w^m)=\sum_{0\le a &lt; d}a_{ap+b}w^{ma} Ab(wm)=0a<daap+bwma
m ∈ [ 0 , d ) m\in[0,d) m[0,d)
那么
A ( g k ) = ∑ 0 ≤ b &lt; p g k b ∑ 0 ≤ a &lt; d a a p + b g a k p = ∑ 0 ≤ b &lt; p g k b A b ( g k p ) = ∑ 0 ≤ b &lt; p g k b A b ( w k ) A(g^k)=\sum_{0\le b&lt; p}g^{kb}\sum_{0\le a&lt;d}a_{ap+b}g^{akp}=\sum_{0\le b&lt; p}g^{kb}A_b(g^{kp})=\sum_{0\le b&lt; p}g^{kb}A_b(w^k) A(gk)=0b<pgkb0a<daap+bgakp=0b<pgkbAb(gkp)=0b<pgkbAb(wk)
一波推导,我们得到了一个式子。
A ( g k ) = ∑ 0 ≤ b &lt; p g k b A b ( w k ) A(g^k)=\sum_{0\le b&lt; p}g^{kb}A_b(w^k) A(gk)=0b<pgkbAb(wk)
美中不足的是 k ∈ [ 0 , L ) k\in [0,L) k[0,L)
这个时候令 k = u d + v k=ud+v k=ud+v
那么
A ( g u d + v ) = ∑ 0 ≤ b &lt; p g ( u d + v ) b A b ( w ( u d + v ) ) A(g^{ud+v})=\sum_{0\le b&lt; p}g^{(ud+v)b}A_b(w^{(ud+v)}) A(gud+v)=0b<pg(ud+v)bAb(w(ud+v))
注意到 w d = g p d = g L = 1 w^d=g^{pd}=g^L=1 wd=gpd=gL=1
于是就有
A ( g u d + v ) = ∑ 0 ≤ b &lt; p g ( u d + v ) b A b ( w v ) A(g^{ud+v})=\sum_{0\le b&lt; p}g^{(ud+v)b}A_b(w^{v}) A(gud+v)=0b<pg(ud+v)bAb(wv)
搞定!
果然还是太菜了,网上的大佬似乎都是直接秒出这个式子,只有蒟蒻用markdown推了半天。。。

代码

#include<bits/stdc++.h>
const int N = 5e5 + 10;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int a[N], b[N], w[N], R[N], pr[101], tot, P, G, n;
int add(int &a, int b) {return a += b, a >= P ? a - P : a;}
int mul(int a, int b) {return 1LL * a * b % P;}
int Pow(int x, int k) {
	int r = 1;
	for(;k; x = mul(x, x), k >>= 1)
		if(k & 1)
			r = mul(r, x);
	return r;
}
bool Ck(int x, int t) {
	for(int i = 1;i <= tot; ++i)
		if(Pow(x, t / pr[i]) == 1)
			return true;
	return false;
}
void Findori() {
	int x = P - 1;
	for(int i = 2;i <= 7; ++i)
		for(;!(x % i); pr[++tot] = i, x /= i) ;
	G = 2;
	for(;Ck(G, n);) ++G;
}
int Pos(int x, int s, int de, int len) {
	if(de == tot + 1) return s;
	int d = len / pr[de], r = x % pr[de];
	return Pos(x / pr[de], s + d * r, de + 1, d);
}
void DFT(int *A) {
	static int B[N];
	for(int i = 0;i < n; ++i)
		B[R[i]] = A[i];
	int *cur = B, *nxt = A;
	for(int d = 1, de = tot; de; d *= pr[de--], std::swap(cur, nxt)) {
		int pd = d * pr[de], p = n / pd; //w(n,m)^p=w(n/p,m)
		for(int st = 0; st < n; st += pd)
			for(int a = 0; a < pd; a += d) // a * d
				for(int b = 0;b < d; ++b) {
					int &ps = nxt[st + a + b], o = (a + b) * p; ps = 0;
					for(int r = 0; r < pr[de]; ++r)
						ps = add(ps, mul(w[1LL * o * r % n], cur[st + r * d + b]));
				}
	}
	if(cur != A)
		memcpy(A, cur, sizeof(int) * n);
}
int main() {
	n = ri(); int C = ri() % n; P = n + 1;
	Findori();
	for(int i = 0;i < n; ++i)
		a[i] = ri() % P;
	for(int i = 0;i < n; ++i)
		b[i] = ri() % P;
	w[0] = 1;
	for(int i = 1;i < n; ++i)
		w[i] = mul(w[i - 1], G);
	for(int i = 1;i < n; ++i)
		R[i] = Pos(i, 0, 1, n);
	DFT(a); DFT(b);
	for(int i = 0;i < n; ++i)
		a[i] = mul(a[i], Pow(b[i], C));
	DFT(a);
	std::reverse(a + 1, a + n);
	for(int i = 0;i < n; ++i)
		printf("%d\n", mul(a[i], n)); // n * n = 1 (mod n + 1)
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值