【NOI2019模擬2019.6.29】智慧樹(fft單位根性質推導)

Description:


在這裏插入圖片描述
1<=n<=8000,m<=57984且m是mo-1的約數

空間限制32MB,時間限制5s

題解:


首先思考一下m=2?m=2^?怎麼做?

我們知道fft的本質是一個循環卷積,這道題要求mod mmod~m,那麼可以利用這個特質而不管溢出,一開始對每一個點的序列進行dft,然後每次轉移就是把dft的序列乘起來,最後逆dft回去就好了。

因爲每個點一開始的序列只有一個位置有值,所以可以O(m)O(m)dft,即A[i]+=wib[x] mod mA[i]+=w^{i*b[x]~mod~m}

空間限制非常小,也就是我們不能把dft的序列存下來,考慮到dft後每一位完全獨立,所以可以先枚舉做哪一位,再在樹上dp。

還有一種方法,這是一個樹形依賴dp,考慮用樹鏈剖分節省空間,先做重兒子,把重兒子做完的數組的指針給當前點,然後做輕兒子,做完一個就合併,由於輕鏈+重鏈條數是log的,所以空間是O(logm)O(log*m)的。

然後最後的問題是m2?m≠2^?怎麼辦,此時的m依然是(mo-1)的約數,也就是存在單位複數根,那麼fft的性質依然可以用,只是不能用分治做fft,那麼不妨看一下式子:
aa dft成AA
A[i]=j=0n1a[j]wijA[i]=\sum_{j=0}^{n-1}a[j]*w^{ij}
A[i]=j=0n1a[j]wCi+j2Ci2Cj2A[i]=\sum_{j=0}^{n-1}a[j]*w^{C_{i+j}^2-C_{i}^2-C_{j}^2}

那麼可以用一次卷積來完成這個dft的,實際上要做的是逆dft,差不多的。

注意常數優化。

Code:


#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i <  B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int mo = 950009857;

ll ksm(ll x, ll y) {
	ll s = 1;
	for(; y; y /= 2, x = x * x % mo)
		if(y & 1) s = s * x % mo;
	return s;
}

const int N = 2e5 + 5;
ll a[N]; int r[N];

void dft(ll *a, int n, int F) {
	ff(i, 0, n) {
		r[i] = r[i / 2] / 2 + (i & 1) * (n / 2);
		if(i < r[i]) swap(a[i], a[r[i]]);
	}
	for(int h = 1; h < n; h *= 2) {
		ll wn = ksm(7, (mo - 1) / 2 / h);
		for(int j = 0; j < n; j += 2 * h) {
			ll b, *l = a + j, *r = a + j + h, w = 1;
			ff(i, 0, h) {
				b = *r * w, *r = (*l - b) % mo, *l = (*l + b) % mo;
				w = w * wn % mo, l ++, r ++;
			}
		}
	}
	if(F == -1) {
		reverse(a + 1, a + n);
		ll v = ksm(n, mo - 2);
		ff(i, 0, n) a[i] = (a[i] + mo) * v % mo;
	}
}
void fft(ll *a, ll *b, int n) {
	dft(a, n, 1); dft(b, n, 1);
	ff(i, 0, n) a[i] = a[i] * b[i] % mo;
	dft(a, n, -1);
 }

int n, m, b[N], x, y;
int fi[N], to[N * 2], nt[N * 2], tot;

void link(int x, int y) {
	nt[++ tot] = fi[x], to[tot] = y, fi[x] = tot;
}

int d[8005], d0, fa[8005];
void dg(int x) {
	d[++ d0] = x;
	for(int i = fi[x]; i; i = nt[i]) if(to[i] != fa[x])
		fa[to[i]] = x, dg(to[i]);
}

int v[N], f[N];
ll w, aw[N];
ll s[N], s2[N * 2], s3[N * 2];

int c[N];

int main() {
	freopen("tree.in", "r", stdin);
	freopen("tree.out", "w", stdout);
	scanf("%d %d", &n, &m);
	w = ksm(7, (mo - 1) / m);
	aw[0] = 1; fo(i, 1, m) aw[i] = aw[i - 1] * w % mo;
	fo(i, 1, n) scanf("%d", &b[i]);
	fo(i, 1, n - 1) {
		scanf("%d %d", &x, &y);
		link(x, y); link(y, x);
	}
	dg(1);
	ff(j, 0, m) {
		fo(i, 1, n) f[i] = aw[c[i]], c[i] = (c[i] + b[i] > m) ? (c[i] + b[i] - m) : (c[i] + b[i]);
		fd(i, d0, 2) {
			s[j] += f[d[i]];
			f[d[i]] ++;
			f[fa[d[i]]] = (ll) f[fa[d[i]]] * f[d[i]] % mo;
		}
		s[j] += f[1];
		s[j] %= mo;
	}
	ll fw = ksm(w, m - 1);
	ff(i, 0, 2 * m) s2[i] = ksm(w, (ll) i * (i - 1) / 2);
	ff(i, 0, m) s[i] = s[i] * ksm(fw, (ll) i * (i - 1) / 2) % mo;
	ff(i, 0, m) s3[m - i] = s[i];
	int tp = 0;
	while(1 << ++ tp <= 3 * m);
	fft(s3, s2, 1 << tp);
	ff(i, 0, m) s[i] = s3[i + m] * ksm(fw, (ll) i * (i - 1) / 2) % mo;
	ff(i, 1, m / 2) swap(s[i], s[m - i]);
	ff(i, 0, m) s[i] = s[i] * ksm(m, mo - 2) % mo;
	
	ff(i, 0, m) pp("%lld ", (s[i] + mo) % mo);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章