题解: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;
}

浙公网安备 33010602011771号