Loading

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;
}

posted @ 2025-07-22 22:12  qkhm  阅读(22)  评论(0)    收藏  举报