【Codechef POLYEVAL Evaluate the polynomial】【FFT】

題意

nn次多項式模pp意義下多點求值。
n250000,p=786433n\le 250000, p=786433

分析

可以直接上O(nlog2n)O(n\log^2n)的多項式多點求值(不可能的)。

首先786433=2183+1786433=2^{18}*3+1,可以取n=218n=2^{18}

注意到,DFT的過程本身就是特殊的多點求值,對應的橫座標分別是ω0,ω,,ωn1\omega^0,\omega,\cdots,\omega^{n-1},其中ω\omegann次單位根。在NTT中,模pp意義下nn次單位根取的是ωn=gp1n=g3\omega_n=g^{\frac{p-1}{n}}=g^3

其中ggpp的一個原根。如果我們直接對多項式做DFT,就可以得到11p1p-1中,離散對數是33的倍數的數的點值。注意到i=0naixi=i=0nairi(xr)i\sum_{i=0}^na_ix^i=\sum_{i=0}^na_ir^i\Big(\frac{x}{r}\Big)^i

如果我們構造新多項式的ii次項係數爲airia_ir^i,求出來xx'的點值,對應的就是原多項式中xrx'r的點值。那麼就把求離散對數爲3k+13k+1的數的點值轉化爲了求新多項式中離散對數爲3k3k的數的點值,直接套用上面的方法。同理離散對數爲3k+23k+2的數的點值也可以這麼做。

總共只需要做三次DFT。注意當x=0x=0時取值爲a0a_0

代碼

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 300005;
const int MOD = 786433;
const int g = 10;

int n, a[N], b[N], c[N], rev[N], L, ans[MOD];

void pre()
{
	int lg = 18; L = 1 << 18;
	for (int i = 0; i < L; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (lg - 1));
}

int ksm(int x, int y)
{
	int ans = 1;
	while (y)
	{
		if (y & 1) ans = (LL)ans * x % MOD;
		x = (LL)x * x % MOD; y >>= 1;
	}
	return ans;
}

void NTT(int * a)
{
	for (int i = 0; i < N; i++) if (i < rev[i]) swap(a[i], a[rev[i]]);
	for (int i = 1; i < L; i <<= 1)
	{
		int wn = ksm(g, (MOD - 1) / i / 2);
		for (int j = 0; j < L; j += (i << 1))
		{
			int w = 1;
			for (int k = 0; k < i; k++)
			{
				int u = a[j + k], v = (LL)a[j + k + i] * w % MOD;
				a[j + k] = (u + v) % MOD; a[j + k + i] = (u + MOD - v) % MOD;
				w = (LL)w * wn % MOD;
			}
		}
	}
}

int main()
{
	scanf("%d", &n);
	for (int i = 0; i <= n; i++) scanf("%d", &a[i]);
	ans[0] = a[0];
	pre();
	for (int i = 0; i <= n; i++) b[i] = (LL)a[i] * ksm(g, i) % MOD, c[i] = (LL)a[i] * ksm(g, i * 2) % MOD;
	NTT(a); NTT(b); NTT(c);
	int t1 = 1, t2 = g, t3 = (LL)g * g % MOD, p = (LL)t3 * g % MOD;
	for (int i = 0; i < L; i++)
	{
		ans[t1] = a[i]; ans[t2] = b[i]; ans[t3] = c[i];
		t1 = (LL)t1 * p % MOD;
		t2 = (LL)t2 * p % MOD;
		t3 = (LL)t3 * p % MOD;
	}
	int q; scanf("%d", &q);
	while (q--)
	{
		int x; scanf("%d", &x);
		printf("%d\n", ans[x]);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章