QOJ4811 Be Careful
考虑
但是这个 dp 不好转移,因为 MEX 不是很好表示。但是如果只考虑子节点中的非叶子节点,那么 MEX 是
考虑阈值分治,把
对于
对于
考虑这个的转移,不妨设填完
注意到第二个 dp 需要知道
考虑怎么求答案,不妨求钦定
设
看起来平衡一下指数是
不妨假设我们当前的
-
对于任意
,子节点中 的不少于 个。 -
对于任意
,子节点中 的不多于 个。
这里的子节点都不包含叶子。证明调整即可。
也就是说,子节点的
于是总复杂度
#include <bits/stdc++.h>
#define ALL(x) begin(x), end(x)
using namespace std;
void file() {
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
}
using ll = long long;
const int kMod = 998244353;
void Add(int& x, int y) { ((x += y) >= kMod) && (x -= kMod); }
void Sub(int& x, int y) { ((x -= y) < 0) && (x += kMod); }
int Sum(int x, int y) { return Add(x, y), x; }
int Dif(int x, int y) { return Sub(x, y), x; }
int Pow(int x, int y) {
int b = x, r = 1;
for(; y; b = (ll)b * b % kMod, y /= 2) {
if(y & 1) r = (ll)r * b % kMod;
}
return r;
}
int Inv(int x) { return Pow(x, kMod - 2); }
const int kN = 205;
int n;
array<int, kN> deg;
array<array<int, kN>, kN> C, S, dp, suf;
array<vector<int>, kN> g;
void Init() {
for(int i = 0; i <= n; i++) {
C[i][0] = 1;
for(int j = 1; j <= i; j++) {
C[i][j] = Sum(C[i - 1][j], C[i - 1][j - 1]);
}
}
S[0][0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= i; j++) {
S[i][j] = ((ll)S[i - 1][j] * j + S[i - 1][j - 1]) % kMod;
}
}
for(int i = 0; i <= n; i++) {
for(int j = 0; j <= n; j++) {
if(!S[i][j]) continue;
for(int k = 2; k <= j; k++) {
S[i][j] = (ll)S[i][j] * k % kMod;
}
}
}
}
int Eval(int x, int fa, int B) {
int ans = B;
for(int to : g[x]) {
if(to != fa) ans += (deg[to] > B);
}
return ans;
}
void Dfs(int x, int fa) {
deg[x] = g[x].size() - !!fa;
if(!deg[x]) return ;
for(int to : g[x]) {
if(to != fa) Dfs(to, x);
}
int B = -1, cnt = n + 1, leaf = 0;
for(int b = 0; b <= n; b++) {
int val = Eval(x, fa, b);
if(cnt > val) cnt = val, B = b;
}
vector<int> small, large;
for(int to : g[x]) {
if(to != fa) {
if(!deg[to]) leaf++;
else {
if(deg[to] <= B) small.push_back(to);
else large.push_back(to);
}
}
}
sort(ALL(small), [&](int x, int y) -> bool { return deg[x] < deg[y]; });
sort(ALL(large), [&](int x, int y) -> bool { return deg[x] < deg[y]; });
vector<int> dp1 {1};
auto Dp1 = [&]() -> void {
vector<int> old {1};
for(int i : small) {
dp1.assign(1 << deg[i] + 1, 0);
for(int msk = 0; msk < old.size(); msk++) {
for(int v = 0; v <= deg[i]; v++) {
Add(dp1[msk | (1 << v)], (ll)old[msk] * dp[i][v] % kMod);
}
}
old = dp1;
}
};
auto Dp2 = [&](int msk, int coe) -> void {
int siz = large.size();
vector<vector<int>> old, dp2;
old.resize(leaf + 1, vector<int> (1 << siz, 0)), dp2 = old;
old[0][0] = dp2[0][0] = 1;
vector<int> pw (leaf + 1, 0);
vector<int> prod (1 << siz, 0);
auto GetAns = [&](int v) -> void {
prod[0] = 1;
for(int i = 0; i < siz; i++) prod[1 << i] = suf[large[i]][v];
for(int msk = 1; msk < (1 << siz); msk++) {
int lb = msk & -msk;
prod[msk] = (ll)prod[msk ^ lb] * prod[lb] % kMod;
}
pw[0] = 1;
for(int i = 1; i <= leaf; i++) {
pw[i] = (ll)pw[i - 1] * (n - v + 1) % kMod;
}
int all = (1 << siz) - 1;
for(int c = 0; c <= min(v, leaf); c++) {
int coef = 0;
for(int i = c; i <= leaf; i++) {
Add(coef, (ll)S[i][c] * C[leaf][i] % kMod * pw[leaf - i] % kMod);
}
coef = (ll)coef * coe % kMod;
for(int msk = 0; msk < (1 << siz); msk++) {
Add(suf[x][v], (ll)prod[all ^ msk] * coef % kMod * old[c][msk] % kMod);
}
}
};
for(int v = 0; v <= deg[x]; v++, old = dp2) {
GetAns(v);
for(int c = 0; c <= min(v, leaf); c++) {
for(int i = 0; i < siz; i++) {
for(int msk = 0; msk < (1 << siz); msk++) {
if(msk & (1 << i)) {
Add(dp2[c][msk], (ll)dp2[c][msk ^ (1 << i)] * dp[large[i]][v] % kMod);
}
}
}
}
for(int c = min(v + 1, leaf); c; c--) {
for(int msk = 0; msk < (1 << siz); msk++) {
Add(dp2[c][msk], dp2[c - 1][msk]);
}
}
if((v > __lg(msk + 1)) || !(msk & (1 << v))) {
for(int c = 0; c <= min(v, leaf); c++) {
for(int msk = 0; msk < (1 << siz); msk++) {
Sub(dp2[c][msk], old[c][msk]);
}
}
}
}
};
Dp1();
for(int msk = 0; msk < dp1.size(); msk++) Dp2(msk, dp1[msk]);
for(int i = 0; i <= deg[x]; i++) {
dp[x][i] = Dif(suf[x][i], suf[x][i + 1]);
}
}
int main() {
// file();
ios::sync_with_stdio(0), cin.tie(0);
cin >> n, Init();
for(int i = 1, u, v; i < n; i++) {
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
Dfs(1, 0);
for(int i = 0; i <= n; i++) cout << dp[1][i] << "\n";
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下