codeforces1054H. Epic Convolution 2dFFT 原根

codeforces1054H. Epic Convolution

题目传送门

分析

直接处理不好处理,考虑转化。
难搞的是 c i 2 j 3 c^{i^2j^3} ci2j3
根据费马小定理,可以让 i 2 j 3 m o d    490018 i^2j^3\mod 490018 i2j3mod490018
发现模数较小,一个基本的思路是,考虑求 ∑ i , j a i b j [ i 2 j 3 ≡ k m o d    490018 ] \sum_{i,j}a_ib_j[i^2j^3\equiv k \mod 490018] i,jaibj[i2j3kmod490018]
乘法不好搞,考虑用原根把乘法转化成加法。
我们假设模数是质数 P P P,原根是 w w w,设 i = w x , j = w y , k = w z i=w^x,j=w^y,k=w^z i=wx,j=wy,k=wz
那么问题转化成
∑ i , j a i b j [ 2 x + 3 y ≡ z m o d    P − 1 ] \sum_{i,j}a_ib_j[2x+3y\equiv z \mod P-1] i,jaibj[2x+3yzmodP1]
只需要将所有的 a i a_i ai映射到 a 2 x a_{2x} a2x,所有的 b j b_j bj映射到 b 3 y b_{3y} b3y上就可以用 F F T FFT FFT解决了。
不是质数直接搞肯定不成,于是考虑分解一下模数然后用 C R T CRT CRT合并
490018 = 2 ⋅ 491 ⋅ 499 490018=2\cdot491\cdot499 490018=2491499
模数很给力,刚好仨质数。
将一个数 i i i分解成 ( i m o d    2 , i m o d    491 , i m o d    499 ) (i\mod 2, i \mod 491, i \mod 499) (imod2,imod491,imod499)
m o d    2 \mod 2 mod2这个玩意儿不用太在意,考虑分奇偶性。奇数的答案一定是奇数卷奇数得到的,偶数的答案用全部的答案减去奇数的答案就好了。
于是每个数 i i i按奇数和偶数分类后转化成二元组 ( i m o d    491 , i m o d    499 ) (i\mod 491, i\mod 499) (imod491,imod499)
然后映射成原根 ( g 1 i 1 , g 2 i 2 ) (g_1^{i_1},g_2^{i_2}) (g1i1,g2i2)
注意这个时候如果是 499 , 491 499,491 499,491的倍数要单独考虑,直接抓出来暴力就可以了,复杂度过得去。
接下来我们要解决的问题变成了一个二维卷积。
也就是
A a , b = ∑ c , d , e , f [ c + e ≡ a m o d    490 ] [ d + f ≡ b m o d    499 ] a c , d ⋅ b e , f A_{a,b}=\sum_{c,d,e,f}[c+e\equiv a \mod 490][d+f \equiv b \mod 499]a_{c,d}\cdot b_{e,f} Aa,b=c,d,e,f[c+eamod490][d+fbmod499]ac,dbe,f
看上去很麻烦。不过我们考虑优化一维。
把每一行看成一个多项式,那么相当于 A a = ∑ c , e [ c + e ≡ a m o d    490 ] a c ∗ b e A_a=\sum_{c,e}[c+e\equiv a \mod 490]a_{c}* b_e Aa=c,e[c+eamod490]acbe
为了方便我们用 ∗ * 表示卷积, ⋅ \cdot 表示乘法,外面的 ∑ \sum 是多项式加法。
考虑先对行 D F T DFT DFT,那么有 [ D F T ( A a ) ] k = ∑ c , e [ c + e ≡ a m o d    490 ] [ D F T ( a c ) ] k ⋅ [ D F T ( b e ) ] k [DFT(A_a)]_k=\sum_{c,e}[c+e\equiv a \mod 490][DFT(a_c)]_k\cdot [DFT(b_e)]_k [DFT(Aa)]k=c,e[c+eamod490][DFT(ac)]k[DFT(be)]k
这个时候问题转化成了每一列的卷积,只不过卷积的东西是对行多项式 D F T DFT DFT完之后的点值。
同样地,我们可以再对列进行 F F T FFT FFT,这样的话就可以转化成
A a , b = a a , b ⋅ b a , b A_{a,b}=a_{a,b}\cdot b_{a,b} Aa,b=aa,bba,b
I D F T IDFT IDFT是完全一样的一个过程。
实际上上面描述的算法叫做 2 d − F F T 2d-FFT 2dFFT,它的公式是:
D F T : : f ( x , y ) = ∑ u = 0 M − 1 ∑ v = 0 N − 1 F ( u , v ) e 2 π i ( x u M + y v N ) DFT::f(x,y)=\sum_{u=0}^{M-1}\sum_{v=0}^{N-1}F(u,v)e^{2\pi i(\frac{xu}{M}+\frac{yv}{N})} DFT::f(x,y)=u=0M1v=0N1F(u,v)e2πi(Mxu+Nyv)
I D F T : : F ( x , y ) = 1 N M ∑ u = 0 M − 1 ∑ v = 0 N − 1 f ( u , v ) e − 2 π i ( x u M + y v N ) IDFT::F(x,y)=\frac{1}{NM}\sum_{u=0}^{M-1}\sum_{v=0}^{N-1}f(u,v)e^{-2\pi i(\frac{xu}{M}+\frac{yv}{N})} IDFT::F(x,y)=NM1u=0M1v=0N1f(u,v)e2πi(Mxu+Nyv)
然后问题就解决啦!
复杂度 O ( K ) O(K) O(K)

代码

代码略长。。。但是应该会比标解的400+行好一丢丢。

#include<bits/stdc++.h>
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;
}
const int L = 1024, N = 1e5 + 10, P = 490019, phi = P - 1;
const double pi = acos(-1.0);
struct cp {
    double r, i;
    cp(double _r = 0, double _i = 0) : r(_r), i(_i) {}
    cp operator + (const cp &a) {return cp(r + a.r, i + a.i);}
    cp operator - (const cp &a) {return cp(r - a.r, i - a.i);}
    cp operator * (const cp &a) {return cp(r * a.r - i * a.i, i *a.r + a.i * r);}
    cp operator / (const double &x) {return cp(r / x, i / x);}
    void operator *= (const cp &x) {*this = *this * x;}
    void operator /= (const double &x) {*this = *this / x;}
}A[2][L][L], B[2][L][L], w[L];
int R[L], pwc[phi], st[N], a[N], b[N], pw1[500], Lg1[500], pw2[500], Lg2[500], c, tp, n, m, Ans;
int Pow(int x, int k, int P) {
	int r = 1;
	for(;k; k >>= 1, x = 1LL * x * x % P)
		if(k & 1) r = 1LL * r * x % P;
	return r;
}
int Iv(int x, int P) {return Pow(x, P - 2, P);}
void Chi(int i, int &x, int &y, int &z)  {x = i & 1; y = i % 491; z = i % 499;}
int iChi(int a, int b, int c) {
	int x = 491LL * 499 * Iv(491 * 499, 2) % phi;
	int y = 2LL * 499 * Iv(2 * 499, 491) % phi;
	int z = 2LL * 491 * Iv(2 * 491, 499) % phi;
	return (1LL * a * x + 1LL * b * y + 1LL * c * z) % phi;
}
void Pre() {
    int x = 9;
    for(int i = 1;i < L; ++i)
        R[i] = R[i >> 1] >> 1 | (i & 1) << x;
    for(int i = 0;i < L; ++i)     
        w[i] = cp(cos(2 * pi * i / L), sin(2 * pi * i / L));
	pwc[0] = 1;
	for(int i = 1;i < phi; ++i)
		pwc[i] = 1LL * pwc[i - 1] * c % P;
	pw1[0] = 1;
	for(int i = 1;i < 490; ++i)
		pw1[i] = pw1[i - 1] * 7 % 491,
		Lg1[pw1[i]] = i;
	pw2[0] = 1;
	for(int i = 1;i < 498; ++i)
		pw2[i] = pw2[i - 1] * 7 % 499,
		Lg2[pw2[i]] = i;
}
void DFT(cp *F, int ri) {
    for(int i = 1;i < L; ++i)
        if(i < R[i])
            std::swap(F[i], F[R[i]]);
    for(int i = 1, d = L >> 1;i < L; i <<= 1, d >>= 1)
        for(int j = 0;j < L; j += i << 1) {
            cp *l = F + j, *r = F + j + i, *p = w, tp;
            for(int k = i; k--; ++l, ++r, p += d)
                tp = *r * *p, *r = *l - tp, *l = *l + tp;
        }
    if(!~ri) {
    	for(int i = 1;i < L >> 1; ++i)
    		std::swap(F[i], F[L - i]);
    	for(int i = 0;i < L; ++i)
    		F[i] /= L;
    }
}
void Tw_DFT(cp F[L][L], int ir) {
	for(int i = 0;i < L; ++i)
		DFT(F[i], ir);
	for(int i = 0;i < L; ++i) {
		static cp tp[L];
		for(int j = 0;j < L; ++j)
			tp[j] = F[j][i];
		DFT(tp, ir);
		for(int j = 0;j < L; ++j)	
			F[j][i] = tp[j];
	}
}
void Solve0() {
	tp = 0;
	for(int i = 0;i < std::max(n, m); i += 491)
		st[++tp] = i;
	for(int i = 0;i < std::max(n, m); i += 499)
		st[++tp] = i;
	std::sort(st + 1, st + tp + 1);
	tp = std::unique(st + 1, st + tp + 1) - st - 1;
	for(int i = 0;i < n; ++i) {
		int i2 = 1LL * i * i % phi; long long sum = 0;
		for(int j = 1;j <= tp && st[j] < m; ++j) {
			int j3 = 1LL * st[j] * st[j] * st[j] % phi;
			sum += 1LL * a[i] * b[st[j]] * pwc[1LL * i2 * j3 % phi] % P;
		}
		Ans = (Ans + sum) % P;
	}
	for(int j = 0;j < m; ++j) {
		int j3 = 1LL * j * j * j % phi; long long sum = 0;
		for(int i = 1;i <= tp && st[i] < n; ++i) {
			int i2 = 1LL * st[i] * st[i] % phi; 
			sum += 1LL * a[st[i]] * b[j] * pwc[1LL * i2 * j3 % phi] % P;
		}
		Ans = (Ans + sum) % P;
	}
	for(int i = 1;i <= tp && st[i] < n; ++i) {
		int i2 = 1LL * st[i] * st[i] % phi; long long sum = 0;
		for(int j = 1;j <= tp && st[j] < m; ++j) {
			int j3 = 1LL * st[j] * st[j] * st[j] % phi;
			sum += 1LL * a[st[i]] * b[st[j]] * pwc[1LL * i2 * j3 % phi] % P;
		}
		Ans = (Ans - sum) % P;
	}
}
int main() {
	n = ri(); m = ri(); c = ri();
	for(int i = 0;i < n; ++i)
		a[i] = ri();
	for(int i = 0;i < m; ++i)
		b[i] = ri();
	Pre(); 
	Solve0();
	for(int i = 0;i < n; ++i) {
		int x, y, z; Chi(i, x, y, z);
		if(!y || !z) continue;
		y = (Lg1[y] << 1) % 490;
		z = (Lg2[z] << 1) % 498;
		A[x][y][z].r += a[i];
		if(x)
			A[0][y][z].r += a[i];
	}
	for(int j = 0;j < m; ++j) {
		int x, y, z; Chi(j, x, y, z);
		if(!y || !z) continue;
		y = (Lg1[y] * 3) % 490;
		z = (Lg2[z] * 3) % 498;
		B[x][y][z].r += b[j];
		if(x)
			B[0][y][z].r += b[j];
	}
	Tw_DFT(A[0], 1); Tw_DFT(A[1], 1);
	Tw_DFT(B[0], 1); Tw_DFT(B[1], 1);
	for(int x = 0;x < 2; ++x)
		for(int y = 0;y < L; ++y)
			for(int z = 0;z < L; ++z)
				A[x][y][z] *= B[x][y][z];
	Tw_DFT(A[0], -1); Tw_DFT(A[1], -1);
	for(int x = 0;x < 2; ++x)
		for(int y = 0;y < L; ++y)
			for(int z = 0;z < L; ++z) {
				long long val = (long long)(A[x][y][z].r + 0.5);
				if(!x)
					val -= (long long)(A[1][y][z].r + 0.5);
				val %= P;
				Ans = (Ans + val * pwc[iChi(x, pw1[y % 490], pw2[z % 498])]) % P;
			}
	printf("%d\n", (Ans + P) % P);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值