$ >AtcoderGrandContest \space 005 F. ?Many?Easy?Problems<$
題目大意 :
有一棵大小為 \(n\) 的樹(shù),對(duì)于每一個(gè) \(k \in[1,n]\) ,求出在所有在樹(shù)中選 \(k\) 個(gè)點(diǎn)的方案對(duì)應(yīng)的包含這 \(k\) 個(gè)點(diǎn)的最小聯(lián)通塊大小之和
\(1 \leq n \leq 2 \times 10^5\)
解題思路 :
容易發(fā)現(xiàn),對(duì)于一組選取方案,包含它的最小聯(lián)通塊是唯一的,不妨考慮每一個(gè)點(diǎn)對(duì)這個(gè)聯(lián)通塊的貢獻(xiàn).
觀察發(fā)現(xiàn),一個(gè)點(diǎn)如果在一個(gè)最小聯(lián)通塊中,當(dāng)且僅當(dāng)有兩個(gè)選取點(diǎn)的簡(jiǎn)單路徑經(jīng)過(guò)它
那么點(diǎn) \(u\) 對(duì) \(k\) 個(gè)點(diǎn)的貢獻(xiàn)就是 \(C_n^k -\sum_{v} C_{sz[v]}^k-C_{n-sz[u]}^k\)
觀察發(fā)現(xiàn)這個(gè)式子只和 \(sz\) 有關(guān),不妨設(shè) \(tot[i]\) 表示 \(sz[u]=i\) 的點(diǎn)的數(shù)量
考慮除根以外的每一個(gè)點(diǎn)只會(huì)在其父親計(jì)算的時(shí)候被減去一個(gè) \(C_{sz[u]}^k\) ,同時(shí)每一種 \(sz[u]\) 都會(huì)在計(jì)算大小為 \(n-sz[u]\) 的子樹(shù)的時(shí)候被減去一次
所以 \(C_{sz[u]}^k\) 的被計(jì)算次數(shù)是 \(tot[sz[u]] + tot[n-sz[u]]\)
那么最終答案的式子就是 \(Ans_j =n \times C_n^j -\sum_{i=1}^n (tot[i]+tot[n-i])\times C_i^j\)
設(shè) \(inv[i]\) 表示 \(i!\) 關(guān)于 \(Mod\) 的逆元,將后面的組合數(shù)拆開(kāi)來(lái)可以得到
\(Ans_j =n \times C_n^j -\sum_{i=1}^n (tot[i]+tot[n-i])\times i! \times inv[j] \times inv[i-j]\)
設(shè) \(A[i] = (tot[i]+tot[n-i])\times i!\) ,則 \(Ans_j = n \times C_n^j \times inv[j] - \sum_{i=1}^nA[i]\times inv[i-j]\), 后者 \(NTT\) 進(jìn)行計(jì)算即可
/*program by mangoyang*/
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){int f = 0, ch = 0; x = 0;for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;if(f) x = -x;
}#define int llconst int N = 1605005, L = 200005, P = 924844033, G = 5;
vector<int> g[N];
int inv[N], iv[N], s[N], f[N], tot[N], sz[N], js[N], n;
inline int Pow(int a, int b){int ans = 1;for(; b; b >>= 1, a = a * a % P)if(b & 1) ans = ans * a % P;return ans;
}
namespace NTT{int rev[N];inline int Getrev(int ned){int lg = 0, len = 1;for(; len <= ned; len <<= 1, lg++);for(int i = 0; i < len; i++) rev[i] = (rev[i>>1] >> 1) | ((i & 1) << (lg - 1));return len; }inline void DFT(int *A, int len, int type){for(int i = 0; i < len; i++) if(i < rev[i]) swap(A[i], A[rev[i]]);for(int k = 2; k <= len; k <<= 1){int w = Pow(G, (P - 1) / k); if(type == -1) w = Pow(w, P - 2);for(int i = 0; i < len; i += k){int now = 1;for(int j = i; j < i + (k >> 1); j++, (now *= w) %= P){int x = A[j], y = (now * A[j+(k>>1)]) % P;A[j] = (x + y) % P, A[j+(k>>1)] = (x - y + P) % P;}} }if(type == -1){int now = Pow(len, P - 2);for(int i = 0; i < len; i++) (A[i] *= now) %= P;}}inline void Times(int *A, int *B, int lena, int lenb){int len = Getrev(lena + lenb + 1);DFT(A, len, 1), DFT(B, len, 1);for(int i = 0; i < len; i++) A[i] = A[i] * B[i] % P;DFT(A, len, -1);}
}
inline void dfs(int u, int fa){sz[u] = 1, f[u] = fa;for(int i = 0; i < g[u].size(); i++)if(g[u][i] != fa) dfs(g[u][i], u), sz[u] += sz[g[u][i]];
}
inline int C(int x, int y){ return js[x] * inv[y] % P * inv[x-y] % P;
}
signed main(){read(n), js[0] = 1, inv[0] = iv[L] = 1;for(int i = 1; i <= n; i++){js[i] = js[i-1] * i % P;iv[L-i] = inv[i] = Pow(js[i], P - 2);}for(int i = 1, x, y; i < n; i++){read(x), read(y);g[x].push_back(y), g[y].push_back(x);}dfs(1, 0);for(int i = 2; i <= n; i++) tot[sz[i]]++;for(int i = 1; i <= n; i++) s[L+i] = (tot[i] + tot[n-i]) * js[i] % P;NTT::Times(s, iv, L + n + 1, L + n + 1);for(int i = 1; i <= n; i++){int A = n * C(n, i) % P;int B = inv[i] * s[2*L+i] % P;printf("%lld\n", ((A - B) % P + P) % P);}return 0;
}
轉(zhuǎn)載于:https://www.cnblogs.com/mangoyang/p/9723310.html
總結(jié)
以上是生活随笔為你收集整理的AtcoderGrandContest 005 F. Many Easy Problems的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。