题解:P14658 积云四月
定义域空间
先求最大值。首先证明,对于关于
:::info[证明]
对于一条路径
:::
有了这个性质,这部分就很简单了。直接 DP,令
对
再来求最小值。这里只取虚树内的点跑 DP 就不对了,我们需要一些别的做法。
从链入手,每个
自然地推广到树上:猜测
显然
而
若
若
我们需要支持:连通块求交,连通块求距离最小值和距离最小对应的单点。
不妨先判断是否有交,我们只要能判断某个点
设
若有交,则深度最小的交点为交集的根,向下沿着公共边即可得到整个交集。为了保证时间复杂度正确,我们只需要保留叶子节点和根来表示整个连通块。
若无交,我们同样对
这里需要证明,距离
:::info[证明]
反证。若存在某条虚树边
:::
求点集的根可以按照 DFS 序排序,求第一个点和最后一个点的 LCA。求叶子同样先排序,判断当前点是否是后一个点的祖先即可。
使用
代码写了一坨,仅供参考。
:::success[主要代码]
int n, m, k[MAXN];
int timer, dfn[MAXN], edfn[MAXN], dep[MAXN];
int top, stk[MAXN];
bool vis[MAXN], visA[MAXN], inA[MAXN], inB[MAXN];
int mxSon[MAXN], mnSon[MAXN];
ll dis[MAXN];
i128 mnv, dp[MAXN], ndp[MAXN], mx[MAXN][2];
ll dp2[MAXN], mn[MAXN][2];
int cntA[MAXN], cntB[MAXN];
vector<int> points, S;
int p[MAXN], L[MAXN], stP[MAXN], stL[MAXN];
vector<pii> T[MAXN];
vector<pair<int, ll>> VT[MAXN];
struct ST {
int f[LOGN][MAXN];
int get(int x, int y) const { return dfn[x] < dfn[y] ? x : y; }
void init() {
for (int i = 1; (1 << i) <= n; ++i)
for (int j = 1; j <= n - (1 << i) + 1; ++j)
f[i][j] = get(f[i - 1][j], f[i - 1][j + (1 << i - 1)]);
}
int query(int l, int r) {
int k = lg2(r - l + 1);
return get(f[k][l], f[k][r - (1 << k) + 1]);
}
} st;
int lca(int x, int y) {
if (x == y) return x;
if (dfn[x] > dfn[y]) swap(x, y);
return st.query(dfn[x] + 1, dfn[y]);
}
ll dist(int x, int y) {
return dis[x] + dis[y] - dis[lca(x, y)] * 2;
}
void dfs1(int u, int faU) {
dfn[u] = ++timer;
st.f[0][timer] = faU;
for (auto [v, w] : T[u]) {
if (v == faU) continue;
dep[v] = dep[u] + 1;
dis[v] = dis[u] + w;
dfs1(v, u);
}
edfn[u] = timer;
}
void dfs2(int u) {
ndp[u] = visA[u] ? dp[u] : -inf1;
mx[u][0] = mx[u][1] = -inf1;
mxSon[u] = 0;
for (auto [v, w] : VT[u]) {
dfs2(v);
i128 val = ndp[v] + w;
if (val >= mx[u][0]) {
mx[u][1] = mx[u][0];
mx[u][0] = val;
mxSon[u] = v;
} else {
chkMax(mx[u][1], val);
}
}
chkMax(ndp[u], mx[u][0]);
}
void dfs3(int u, i128 val) {
chkMax(ndp[u], val);
for (auto [v, w] : VT[u])
dfs3(v, max({visA[u] ? dp[u] : -inf1, mx[u][mxSon[u] == v], val}) + w);
}
void dfs4(int u) {
for (auto [v, w] : VT[u]) {
dfs4(v);
cntA[u] += cntA[v];
cntB[u] += cntB[v];
}
}
void dfs5(int u) {
bool leaf = true;
for (auto [v, w] : VT[u]) {
if (inA[v] && inB[v]) {
leaf = false;
dfs5(v);
}
}
if (leaf && S.back() != u) S.emplace_back(u);
}
void dfs6(int u) {
dp2[u] = visA[u] ? 0 : inf2;
mn[u][0] = mn[u][1] = inf2;
mnSon[u] = 0;
for (auto [v, w] : VT[u]) {
dfs6(v);
ll val = dp2[v] + w;
if (val <= mn[u][0]) {
mn[u][1] = mn[u][0];
mn[u][0] = val;
mnSon[u] = v;
} else {
chkMin(mn[u][1], val);
}
}
chkMin(dp2[u], mn[u][0]);
}
void dfs7(int u, ll val) {
chkMin(dp2[u], val);
for (auto [v, w] : VT[u])
dfs7(v, min({visA[u] ? 0 : inf2, mn[u][mnSon[u] == v], val}) + w);
}
int build(const vector<int> &vec) {
auto addPoint = [&](int u) {
if (vis[u]) return;
vis[u] = true;
points.emplace_back(u);
};
auto addEdge = [&](int u, int v) {
VT[u].emplace_back(v, dist(u, v));
addPoint(u);
addPoint(v);
};
stk[top = 1] = vec[0];
addPoint(vec[0]);
int sz = vec.size();
for (int i = 1; i < sz; ++i) {
int u = vec[i];
int d = lca(stk[top], u);
while (top >= 2 && dep[stk[top - 1]] >= dep[d]) {
addEdge(stk[top - 1], stk[top]);
--top;
}
if (d != stk[top]) {
addEdge(d, stk[top]);
stk[top] = d;
}
stk[++top] = u;
}
for (int i = top - 1; i; --i) addEdge(stk[i], stk[i + 1]);
return stk[1];
}
ostream &operator<<(ostream &ot, i128 x) {
static char tmp[40], *pt;
pt = tmp;
while (*pt++ = (x % 10) ^ 48, x /= 10);
while (pt-- != tmp) ot << *pt;
return ot;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i < n; ++i) {
int u, v, w;
cin >> u >> v >> w;
T[u].emplace_back(v, w);
T[v].emplace_back(u, w);
}
dfs1(1, 0);
st.init();
auto cmp = [&](int x, int y) {
return dfn[x] < dfn[y];
};
cin >> m;
for (int i = 1, curP = 1, curL = 1; i <= m; ++i) {
cin >> k[i];
stP[i] = curP;
stL[i] = curL;
for (int j = curP; j <= curP + k[i] - 1; ++j) cin >> p[j];
sort(p + curP, p + curP + k[i], cmp);
k[i] = unique(p + curP, p + curP + k[i]) - (p + curP);
for (int j = curP; j < curP + k[i] - 1; ++j) {
int u = p[j], v = p[j + 1];
if (dfn[v] > edfn[u]) L[curL++] = u;
}
curP += k[i];
L[curL++] = p[curP - 1];
if (i == m) {
stP[m + 1] = curP;
stL[m + 1] = curL;
}
}
S = vector<int>(p + 1, p + stP[2]);
for (int i = 2; i <= m; ++i) {
auto clr = [&]() {
for (int u : points) {
vis[u] = false;
VT[u].clear();
}
points.clear();
};
vector<int> vec;
vec.resize(k[i - 1] + k[i]);
merge(p + stP[i - 1], p + stP[i], p + stP[i], p + stP[i + 1], vec.begin(), cmp);
vec.erase(unique(vec.begin(), vec.end()), vec.end());
int rt = build(vec);
for (int j = stP[i - 1]; j < stP[i]; ++j) visA[p[j]] = true;
dfs2(rt);
dfs3(rt, -inf1);
for (int j = stP[i]; j < stP[i + 1]; ++j) dp[p[j]] = ndp[p[j]];
for (int j = stP[i - 1]; j < stP[i]; ++j) visA[p[j]] = false;
clr();
vec.resize(S.size() + k[i]);
merge(S.begin(), S.end(), p + stP[i], p + stP[i + 1], vec.begin(), cmp);
vec.erase(unique(vec.begin(), vec.end()), vec.end());
rt = build(vec);
for (int j = 0; j < S.size() - 1; ++j) {
int u = S[j], v = S[j + 1];
if (dfn[v] > edfn[u]) ++cntA[u];
}
++cntA[S.back()];
for (int j = stL[i]; j < stL[i + 1]; ++j) ++cntB[L[j]];
int rtA = lca(S.front(), S.back()), rtB = lca(p[stP[i]], p[stP[i + 1] - 1]);
dfs4(rt);
bool inter = false;
int rtInter = 0;
for (int u : points) {
inA[u] = dfn[u] >= dfn[rtA] && dfn[u] <= edfn[rtA] && cntA[u];
inB[u] = dfn[u] >= dfn[rtB] && dfn[u] <= edfn[rtB] && cntB[u];
if (inA[u] && inB[u]) {
inter = true;
if (!rtInter || dep[u] < dep[rtInter]) rtInter = u;
}
}
if (inter) {
S.clear();
S.emplace_back(rtInter);
dfs5(rtInter);
} else {
for (int u : points)
if (inA[u])
visA[u] = true;
dfs6(rt);
dfs7(rt, inf2);
ll mnd = inf2;
int mnp = 0;
for (int u : points) {
if (inB[u] && dp2[u] < mnd) {
mnd = dp2[u];
mnp = u;
}
}
mnv += mnd;
S.clear();
S.emplace_back(mnp);
for (int u : points)
if (inA[u])
visA[u] = false;
}
for (int u : points) cntA[u] = cntB[u] = 0;
clr();
}
i128 mxv = -inf1;
for (int i = stP[m]; i < stP[m + 1]; ++i) chkMax(mxv, dp[p[i]]);
cout << mxv - mnv + 1;
return 0;
}
:::