题解:P12038 [USTCPC 2025] 送温暖

提供一个简单的 \(\mathcal{O}(2^{\lfloor n/2\rfloor}n)\) 做法。

枚举连通块的根节点 \(r\),将 \(sub_u\) 内的点按 DFS 序折半分成 \(L,R\) 两部分,其中 \(|L|=\left\lceil\dfrac{sz_u}2\right\rceil,|R|=\left\lfloor\dfrac{sz_u}2\right\rfloor\)。显然合法的连通块要满足:对于每个 \(u\neq r\),若 \(u\) 在连通块内则 \(fa_u\) 也在连通块内。考虑取 \(u\in S\subseteq L\) 和 \(T\subseteq R\),那么 \(S\cup T\) 是一个合法的连通块,当且仅当:

  • \(\forall u\in S\land u\neq r,fa_u\in S\)。
  • \(\forall u\in T\land fa_u\in R,fa_u\in T\)。
  • \(\forall u\in T\land fa_u\in L,fa_u\in S\)。

前两个条件是容易处理的,考虑如何处理第三个条件。设 \(T\) 中 DFS 序最小的点为 \(p\),观察到若 \(fa_u\in S,u\in T\),则 \(fa_u\) 必然是 \(p\) 的祖先。于是考虑取出 \(p\) 到 \(r\) 的链,我们只需要记录 \(S\) 中点在这条链上 DFS 序最大的点 \(p_S\) 和 \(T\) 中点的父亲在这条链上 DFS 序最大的点 \(p_T\),保证 \(dfn_{p_T}\leq dfn_{p_S}\) 即可。

将所有合法的 \(S,T\) 按 \(dfn_{p_S},dfn_{p_T}\) 分组,用一个 set 维护所有 \(T\) 的点权和,从小到大枚举 \(dfn_{p_S}\) 即可。

搜出满足条件的 \(S\) 可以直接记录当前 DFS 序 \(t=dfn_u\),若选择 \(u\) 则令 \(t\gets t+1\),否则令 \(t\gets dfn_u+sz_u\),\(T\) 同理。

时间复杂度为 \(\mathcal{O}(\sum2^{\lfloor sz_u/2\rfloor}sz_u)=\mathcal{O}(2^{\lfloor n/2\rfloor}n)\)。

代码
#include <bits/stdc++.h>

using namespace std;

using ll = long long;
using i128 = __int128;
using ui = unsigned int;
using ull = unsigned long long;
using u128 = unsigned __int128;
using ld = long double;
using pii = pair<int, int>;
const int MAXN = 34;

template<typename T> T lowbit(T x) { return x & -x; }
template<typename T> void chkMin(T &x, T y) { x = y < x ? y : x; }
template<typename T> void chkMax(T &x, T y) { x = x < y ? y : x; }
constexpr int lg2(ll x) { return 63 ^ __builtin_clzll(x); }
constexpr ll bitCeil(ll x) { return x == 1 ? 1ll : 1ll << lg2(x - 1) + 1; }

int n, mod, fa[MAXN], a[MAXN];
int stmp, dfn[MAXN], rdfn[MAXN], sz[MAXN];
vector<int> T[MAXN];

void dfs(int u) {
	rdfn[dfn[u] = ++stmp] = u;
	sz[u] = 1;
	for (int v : T[u]) {
		dfs(v);
		sz[u] += sz[v];
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin >> n >> mod;
	for (int i = 2; i <= n; ++i) {
		cin >> fa[i];
		T[fa[i]].emplace_back(i);
	}
	for (int i = 1; i <= n; ++i) cin >> a[i];

	dfs(1);

	int ans = *max_element(a + 1, a + n + 1);
	for (int r = 1; r <= n; ++r) {
		int k = sz[r];
		if (k == 1) continue;

		vector<int> mark(n + 1, false);
		int mid = dfn[r] + (k + 1 >> 1), p = rdfn[mid];
		while (p != r) mark[p = fa[p]] = true;

		vector<vector<int>> sl(k + 1 >> 1), sr(k + 1 >> 1);

		auto dfsL = [&](auto &&self, int L, int R, int sum, int mx) -> void {
			if (L >= R) {
				sl[mx].emplace_back(sum);
				return;
			}

			int u = rdfn[L];
			if (u != r) self(self, L + sz[u], R, sum, mx);
			self(self, L + 1, R, (sum + a[u]) % mod, mark[u] ? L - dfn[r] : mx);
		};
		dfsL(dfsL, dfn[r], mid, 0, 0);

		auto dfsR = [&](auto &&self, int L, int R, int sum, int mx) -> void {
			if (L >= R) {
				sr[mx].emplace_back(sum);
				return;
			}
			int u = rdfn[L];
			self(self, L + sz[u], R, sum, mx);
			self(self, L + 1, R, (sum + a[u]) % mod, max(mx, mark[fa[u]] ? dfn[fa[u]] - dfn[r] : 0));
		};
		dfsR(dfsR, mid, dfn[r] + sz[r], 0, 0);

		set<int> s = {0};
		for (int i = 0; i < (k + 1 >> 1); ++i) {
			for (int x : sr[i]) s.insert(x);
			for (int x : sl[i]) chkMax(ans, x + *--s.lower_bound(mod - x));
		}
	}

	cout << ans;
	return 0;
}
posted @ 2026-11-03 21:28  P2441M  阅读(7)  评论(0)    收藏  举报