icpc2017西安 D Islands(树链剖分+分治ntt)

题意

给一棵 n n n 个点的树,每条边可以断开,也可以不断开,求和 1 1 1 所在的连通块大小为 k k k 的方案数, k = 1 , 2 , 3 , . . . , n k=1,2,3,...,n k=1,2,3,...,n。答案对 1811939329 1811939329 1811939329 取模。
n ≤ 1 0 5 n\le 10^5 n105

分析

F u [ x n ] F_u[x^n] Fu[xn] 表示 u u u f a u fa_u fau 贡献连通块大小为 n n n 的方案数。
因此有 F u = F u [ x 0 ] + x ∏ ( u , v ) F v F_u=F_u[x^0]+x\prod\limits_{(u,v)}F_v Fu=Fu[x0]+x(u,v)Fv
其中, F u [ x 0 ] = 2 s z u − 1 F_{u}[x^0]=2^{sz_u-1} Fu[x0]=2szu1,表示 ( u , f a u ) (u,fa_u) (u,fau) 这条边断开, u u u 的子树里的边随便断的方案数。
然后树剖,令 G u = x ∏ ( u , v ) , v ≠ s o n u F v , K u = ∏ ( u , v ) , v ≠ s o n u 2 K v G_u=x\prod\limits_{(u,v),v\neq son_u}F_v,K_u=\prod\limits_{(u,v),v\neq son_u}2K_v Gu=x(u,v),v=sonuFv,Ku=(u,v),v=sonu2Kv G u G_u Gu 可以用分治 n t t ntt ntt O ( n l o g 2 n ) O(nlog^2n) O(nlog2n) 内求出。
然后考虑一条重链的合并,有以下递推式:
[ F u H u ] = [ G u G u 0 2 K u ] [ F s o n u H s o n u ] \begin{bmatrix}F_u\\H_u\end{bmatrix}=\begin{bmatrix}G_u&G_u\\ 0 & 2K_u\end{bmatrix}\begin{bmatrix}F_{son_u}\\H_{son_u}\end{bmatrix} [FuHu]=[Gu0Gu2Ku][FsonuHsonu]
H u H_u Hu 即为 F u [ x 0 ] F_{u}[x^0] Fu[x0]
然后分治求矩阵乘积就可以了,这部分的复杂度也是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n) 的。
写得好不详细啊=.=,建议不会的先去学一下树剖分治ntt。这是一道例题
代码如下。

#include <bits/stdc++.h>
#define all(x) x.begin(), x.end()
#define pii pair<int, int>
#define fi first
#define se second
#define sz(x) x.size()
using namespace std;
typedef long long ll;

void debug_out(){
    cerr << endl;
}
template<typename Head, typename... Tail>
void debug_out(Head H, Tail... T){
    cerr << " " << to_string(H);
    debug_out(T...);
}
#ifdef local
#define debug(...) cerr<<"["<<#__VA_ARGS__<<"]:",debug_out(__VA_ARGS__)
#else
#define debug(...) 55
#endif


typedef unsigned int uint;
namespace Poly {
	typedef vector<uint> poly;
	constexpr uint Max_size = 1 << 21 | 5;
	constexpr int gn = 13;
	const int mod = 1811939329;
	ll power(ll a, ll b) { ll res = 1; while (b) { if (b & 1)res = res * a % mod; a = a * a % mod; b >>= 1; }return res; }
	inline uint mf(uint x)
	{
		return (static_cast<ll>(x) << 32) / mod;
	}
	int sz;
	uint w[Max_size], w_mf[Max_size];
	inline void init(int n)
	{
		for (sz = 2; sz < n; sz <<= 1);
		uint pr = power(gn, (mod - 1) / sz);
		w[sz / 2] = 1, w_mf[sz / 2] = mf(w[sz / 2]);
		for (int i = 1; i < sz / 2; ++i)
			w[sz / 2 + i] = (ll)w[sz / 2 + i - 1] * pr % mod, w_mf[sz / 2 + i] = mf(w[sz / 2 + i]);
		for (int i = sz / 2 - 1; i; --i)
			w[i] = w[i << 1], w_mf[i] = w_mf[i << 1];
	}
	inline void NTT(poly& A, const int L)
	{
		for (int d = L >> 1; d; d >>= 1)
			for (int i = 0; i != L; i += d << 1)
				for (int j = 0; j != d; ++j)
				{
					uint x = (A[i + j] + A[i + d + j])%mod;
					ll t = (A[i + j] + mod - A[i + d + j]) % mod;
					ll q = t * w_mf[d + j] >> 32;
					uint y = (t * w[d + j] - q * mod)%mod;
					A[i + j] = x, A[i + d + j] = y;
				}
	}
	inline void INTT(poly& A, const int L)
	{
		for (int d = 1; d != L; d <<= 1)
			for (int i = 0; i != L; i += d << 1)
				for (int j = 0; j != d; ++j)
				{
					ll x = A[i + j];
					ll y = A[i + d + j];
					ll q = y * w_mf[d + j] >> 32;
					ll t = y * w[d + j] - q * mod;
					A[i + j] = (x + t)%mod, A[i + d + j] = (x + mod - t % mod) % mod;
				}
		reverse(A.begin() + 1, A.end());
		int k = __builtin_ctz(L);
		for (int i = 0; i != L; ++i)
		{
			ll m = -A[i] & (L - 1);
			A[i] = (A[i] + m * mod) >> k;
		}
	}
	poly operator*(poly a, poly b) {
		int n = a.size() + b.size() - 1, R = 2;
		if (n <= 0)return {};
		for (; R <= n; R <<= 1); init(R);
		a.resize(R), b.resize(R);
		NTT(a, R); NTT(b, R);
		for(int i = 0; i < R; i++) a[i] = 1ll * b[i] * a[i] % mod;
		INTT(a, R); a.resize(n);
		for(int i = 0; i < n; i++) if (a[i] >= mod)a[i] -= mod;
		return a;
	}
	poly operator*(uint a, poly b) {
		for (auto& I : b)I = (ll)I * a % mod;
		return b;
	}
	poly operator+(poly a, poly b) {
		int n = max(a.size(), b.size());
		a.resize(n), b.resize(n);
		for(int i = 0; i < n; i++) a[i] = (a[i]+b[i])%mod;
		return a;
	}
	poly operator-(poly a, poly b) {
		int n = max(a.size(), b.size());
		a.resize(n), b.resize(n);
		for(int i = 0; i < n; i++)a[i] = (a[i] + mod - b[i])%mod;
		return a;
	}
}; using namespace Poly;
const int N = 2e5 + 5;
vector<int> E[N];
int siz[N], Fa[N], son[N], dfn[N], top[N], dft;
void dfs1(int u, int pre){
	siz[u] = 1;
	for(int& v: E[u]){
		if(v == pre) continue;
		dfs1(v, u);
		Fa[v] = u;
		siz[u] += siz[v];
		if(siz[v] >= siz[son[u]]) son[u] = v;
	}
}
void dfs2(int u, int f){
	top[u] = f, dfn[++dft] = u;
	if(son[u]) dfs2(son[u], f);
	for(int& v: E[u]) if(v != Fa[u] && v != son[u]) dfs2(v, v);
}
struct Chain{
	poly f0, f1;
	uint k;
};
Chain operator * (Chain a, Chain b){
	Chain c;
	c.f0 = a.f0 * b.f0;
	c.f1 = a.f0 * b.f1 + b.k * a.f1;
	c.k = (ll)a.k * b.k % mod;
	return c;
}
poly merge(vector<poly>& g, int l, int r){
	if(l == r) return g[l];
	int m = l + r >> 1;
	return merge(g, l, m) * merge(g, m + 1, r);
}
Chain merge(vector<Chain>& g, int l, int r){
	if(l == r) return g[l];
	int m = l + r >> 1;
	Chain a = merge(g, l, m), b = merge(g, m + 1, r);
	return a * b;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while(T--){
		dft = 0;
		int n;
		cin >> n;
		for(int i = 1, u, v; i < n; i++){
			cin >> u >> v;
			E[u].push_back(v);
			E[v].push_back(u);
		}
		dfs1(1, 0);
		dfs2(1, 1);
		vector<poly> f(n + 1, {0, 1});
		poly k(n + 1, 1);
		for(int i = n; i >= 1; i--){
			int x = dfn[i];
			vector<Chain> chain;
			if(x == top[x]){
				for(int u = x; top[u] == x; u = son[u]){
					vector<poly> g(1, f[u]);
					for(int& v: E[u]){
						if(v == son[u] || v == Fa[u]) continue;
						f[v][0] = k[v];
						k[u] = (ll)2 * k[u] * k[v] % mod;
						g.push_back(f[v]);
					}
					poly tmp = merge(g, 0, g.size() - 1);
					Chain res;
					res.f0 = res.f1 = tmp;
					res.k = k[u] * 2 % mod;
					chain.push_back(res);
				}
				if(chain.size() > 1){
					poly w = {0, 1};
					Chain tmp = merge(chain, 0, chain.size() - 2);
					f[x] = tmp.f0 * w + tmp.f1;
					k[x] = tmp.k;
				}
			}
		}
		f[1].resize(n + 1);
		for(int i = 1; i <= n; i++) cout << f[1][i] << (i == n? "\n": " ");
		for(int i = 1; i <= n; i++) E[i].clear(), siz[i] = 0, Fa[i] = 0, son[i] = 0, top[i] = 0, dfn[i] = 0;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值