题意
有\(n\)个点形成一棵树,边有权值。\(m\)次询问,每次询问,给出\(k\)个关键点,问把这些关键点都与\(1\)号节点断开(不连通)的最小断边的权值和。保证\(1\)号节点不是关键点。
\(n,m\le 250000,\sum k\le500000\)
题解
考虑只有一次询问怎么做。
直接树形\(dp\)就行了。用\(dp[u]\)表示把\(u\)这棵子树断开的最小代价。对于关键点必须断\(u\)到父亲的边。否则要么断当前点到根的路径上边权最小值,要么断子树内的边(代价为:子树内有关键点的儿子的\(dp\)值之和)。
我们可以把\(1\)号根节点到父亲的边权设为\(+\infty\),那么\(dp[1]\)就是答案。
然后考虑多组数据,发现\(\sum k\le 500000\),显然每次我们需要\(O(k)/O(k\log k)\)的算法。
那么就只需要建虚树\(DP\)就完事了,每次\(O(k\log k+k)\)。这道题还有一个性质,就是虚树中关键点子树中的关键点可忽略。建虚树的时候没有必要建出来。
CODE
#include <cstdio> #include <vector> #include <cctype> #include <cstring> #include <algorithm> using namespace std; inline void read(int &x) { int flg = 1; char ch; while(!isdigit(ch=getchar())) if(ch=='-')flg=-flg; for(x = ch-'0'; isdigit(ch=getchar()); x = x*10+ch-'0'); x *= flg; } typedef long long LL; #define il inline const int MAXN = 250005; int n, q, m; int fir[MAXN], to[MAXN<<1], nxt[MAXN<<1], wt[MAXN<<1], cnt; LL val[MAXN]; il void link(int u, int v, int w) { to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; wt[cnt] = w; to[++cnt] = u; nxt[cnt] = fir[v]; fir[v] = cnt; wt[cnt] = w; } int sz[MAXN], fa[MAXN], top[MAXN], dep[MAXN], son[MAXN], dfn[MAXN], tmr; void dfs1(int u, int ff) { dep[u] = dep[fa[u] = ff] + (sz[u] = 1); for(int i = fir[u], v; i; i = nxt[i]) if((v=to[i]) != ff) { val[v] = min(1ll*wt[i], val[u]); dfs1(v, u); sz[u] += sz[v]; if(sz[v] > sz[son[u]]) son[u] = v; } } void dfs2(int u, int tp) { top[u] = tp; dfn[u] = ++tmr; if(son[u]) dfs2(son[u], tp); for(int i = fir[u], v; i; i = nxt[i]) if((v=to[i]) != fa[u] && v != son[u]) dfs2(v, v); } il int Lca(int u, int v) { while(top[u] != top[v]) { if(dep[top[u]] > dep[top[v]]) u = fa[top[u]]; else v = fa[top[v]]; } return dep[u] > dep[v] ? v : u; } int c[MAXN], stk[MAXN], indx; vector<int>e[MAXN]; il bool cmp(int i, int j) { return dfn[i] < dfn[j]; } il void ins(int x) { if(indx == 1) { stk[++indx] = x; return; } int lca = Lca(x, stk[indx]); if(lca == stk[indx]) return; //此题性质 while(indx > 1 && dfn[stk[indx-1]] >= dfn[lca]) e[stk[indx-1]].push_back(stk[indx]), --indx; if(lca != stk[indx]) e[lca].push_back(stk[indx]), stk[indx] = lca; stk[++indx] = x; } LL dp(int u) { if(!e[u].size()) return val[u]; LL re = 0; for(int i = e[u].size()-1; i >= 0; --i) re += dp(e[u][i]); e[u].clear(); return min(re, val[u]); } int main () { read(n); for(int i = 1, x, y, z; i < n; ++i) read(x), read(y), read(z), link(x, y, z); val[1] = 1ll<<60; dfs1(1, 0); dfs2(1, 1); read(q); while(q--) { read(m); for(int i = 1; i <= m; ++i) read(c[i]); sort(c + 1, c + m + 1, cmp); stk[indx=1] = 1; //1号点必须放进来 for(int i = 1; i <= m; ++i) ins(c[i]); while(indx > 1) e[stk[indx-1]].push_back(stk[indx]), --indx; printf("%lld\n", dp(1)); } }
来源:https://www.cnblogs.com/Orz-IE/p/12149366.html