MX galaxy Day9
P12558 [UOI 2024] Heroes and Monsters
发现选择集合 \(\boldsymbol{与顺序无关}\) ,所以将 \(a\) , \(b\) 升序排列后考虑。
我们并不关心如何让选出的集合合法,那么如果我们已经确定了选择那些,最好的方法就是将 \(b\) 就前 \(k\) 个与 \(a\) 顺次配对, 后 \(n - k + 1\) 个同样如此,形式如同下图:
然后我们就可以设计一个 \(O(n^3)\) 的 \(DP\) ,枚举选出集合的大小。
记状态 \(dp[i][j]\) 表示考虑前 \(i\) 个人,集合内选择了 \(j\) 个人的方案数。
那么如果 \(a[i] > b[j]\) ,则 \(dp[i][j] \leftarrow dp[i - 1][j - 1]\) 。
如果 \(a[i] < b[k + i - j]\) ,则 \(dp[i][j] \leftarrow dp[i - 1][j]\) 。
发现 \(k\) 是转移需要的,必须枚举,这条路是走不了了。
考虑优化,发现正序考虑选入集合和逆序考虑不选入集合是对称的,我们想要将一个前缀和后缀拼起来。
但并不是每个拼接点都合法,我们处理每个 \(k\) 的结果,想要让这个拼接点 \(p\) 满足:
\(\forall\) \(i < p\) \(\wedge\) \(i\notin S\) , \(a_i < b_{k+1}\) , \(\forall\) \(i \ge p\) \(\wedge\) \(i\in S\) , \(a_i > b_k\) 。
所以第一个满足 \(a_i > b_k\) 的位置 \(i\) 就是拼接点。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 1e4 + 7;
const int mod = 998244353;
typedef long long ll;
int n, q, a[_], b[_]; ll f[_][_], g[_][_], ans[_];
int main() {
scanf("%d", & n);
lep(i, 1, n) scanf("%d", a + i);
lep(i, 1, n) scanf("%d", b + i);
std::sort(a + 1, a + 1 + n), std::sort(b + 1, b + 1 + n);
lep(i, 0, n + 1) f[i][0] = g[i][0] = 1;
lep(i, 1, n) lep(j, 1, i)
if (a[i] > b[j]) f[i][j] = (f[i - 1][j] + f[i - 1][j - 1]) % mod;
rep(i, n, 1) lep(j, 1, n - i + 1)
if (a[i] < b[n - j + 1]) g[i][j] = (g[i + 1][j] + g[i + 1][j - 1]) % mod;
lep(k, 0, n) {
int pos = n + 1;
lep(i, 1, n) if (a[i] > b[k]) { pos = i; break; }
lep(i, 0, k) if (n - pos - k + i + 1 >= 0)
ans[k] = (ans[k] + f[pos - 1][i] * g[pos][n - pos - k + i + 1] % mod) % mod;
ans[k] = (ans[k] + ans[k - 1]) % mod;
}
scanf("%d", & q); int l, r; ll res;
while (q--) {
scanf("%d%d", & l, & r);
if (l) res = (ans[r] - ans[l - 1]) % mod;
else res = ans[r];
res = (res + mod) % mod;
printf("%lld\n", res);
}
return 0;
}
CF1799H Tree Cutting
看到 \(k\) 很小,压位。
我们强制要求每次切割都保留深度更小的连通块,最后换根处理。
这样我们就会按照深度降序执行所有操作,树形 \(DP\)。
记 \(f[u, S]\) 为 \(u\) 子树内进行了 \(S\) 集合的操作。
继承儿子状态可以类似背包地转移,然后考虑 \(u\rightarrow fa_u\) 的边是否选。
而只有当 \(sum_S = siz_u\) 时可以选。
然后我们就处理出了以 \(1\) 为根的贡献。
换根和上述过程类似,只不过转换一下父亲儿子关系。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 5000 + 7;
const int mod = 998244353;
typedef long long ll;
int n, sz[_], k, a[10], d[10], U, sum[100]; ll f[_][100], g[_][100], tmp[_][100], ans;
std::vector <int> e[_];
ll Mypow(ll a, ll b) { ll ans = 1;
for (; b; b >>= 1, a = a * a % mod)
if (b & 1) ans = ans * a % mod;
return ans;
}
void prt(int x) { std::bitset <3> B(x); std::cout << B << ' '; }
int lowbit(int x) { return x & -x; }
int hgtbit(int x) { return 31 - __builtin_clz(x); }
void Dfs1(int u, int fa) {
sz[u] = 1, f[u][0] = 1;
for (int v : e[u]) if (v != fa) {
Dfs1(v, u), sz[u] += sz[v];
rep(S, U, 1) for (int T = (S - 1) & S; ; T = (T - 1) & S) {
f[u][S] = (f[u][S] + f[u][T] * f[v][S ^ T] % mod) % mod;
if (!T) break;
}
}
lep(S, 0, U) g[u][S] = f[u][S];
rep(S, U, 1) { int t = hgtbit(S), T = S ^ (1 << t);
if (sz[u] == sum[S])
f[u][S] = (f[u][S] + f[u][T]) % mod;
}
}
void Dfs2(int u, int fa) {
lep(S, 0, U) tmp[u][S] = g[u][S]; ans = (ans + g[u][U]) % mod;
for (int v : e[u]) if (v != fa) {
lep(S, 1, U) for (int T = (S - 1) & S; ; T = (T - 1) & S) {
g[u][S] = (g[u][S] - g[u][T] * f[v][S ^ T] % mod) % mod;
if (!T) break;
}
rep(S, U, 1) { int t = hgtbit(S), T = S ^ (1 << t);
if (n - sz[v] == sum[S])
g[u][S] = (g[u][S] + g[u][T]) % mod;
}
rep(S, U, 1) for (int T = (S - 1) & S; ; T = (T - 1) & S) {
g[v][S] = (g[v][S] + g[v][T] * g[u][S ^ T] % mod) % mod;
if (!T) break;
}
lep(S, 0, U) g[u][S] = tmp[u][S];
}
for (int v : e[u]) if (v != fa) Dfs2(v, u);
}
int main() {
scanf("%d", & n); int u, v;
lep(i, 2, n) scanf("%d%d", & u, & v),
e[u].push_back(v), e[v].push_back(u);
scanf("%d", & k); U = (1 << k) - 1;
lep(i, 0, k - 1) scanf("%d", a + i);
rep(i, k - 1, 1) d[i] = a[i - 1] - a[i]; d[0] = n - a[0];
lep(i, 0, k - 1) sum[1 << i] = d[i];
lep(i, 1, U) sum[i] = sum[i ^ lowbit(i)] + sum[lowbit(i)];
Dfs1(1, 0); lep(S, 0, U) g[1][S] = f[1][S];
Dfs2(1, 0);
ans = ans * Mypow(a[k - 1], mod - 2) % mod;
ans = (ans + mod) % mod;
printf("%lld\n", ans);
return 0;
}
时间仓促,如有错误欢迎指出,欢迎在评论区讨论,如对您有帮助还请点个推荐、关注支持一下

DP 杂题
浙公网安备 33010602011771号