2286: [Sdoi2011]消耗战
Time Limit: 20 Sec Memory Limit: 512 MB
Submit: 4896 Solved: 1824
[ Submit][ Status][ Discuss]
Description
Input
第一行一个整数n,代表岛屿数量。
接下来n-1行,每行三个整数u,v,w,代表u号岛屿和v号岛屿由一条代价为c的桥梁直接相连,保证1<=u,v<=n且1<=c<=100000。
第n+1行,一个整数m,代表敌方机器能使用的次数。
接下来m行,每行一个整数ki,代表第i次后,有ki个岛屿资源丰富,接下来k个整数h1,h2,…hk,表示资源丰富岛屿的编号。
Output
输出有m行,分别代表每次任务的最小代价。
Sample Input
10 1 5 13 1 9 6 2 1 19 2 4 8 2 3 91 5 6 8 7 5 4 7 8 31 10 7 9 3 2 10 6 4 5 7 8 3 3 9 4 6
Sample Output
12 32 22
HINT
对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1
Source
![](https://images2018.cnblogs.com/blog/1230705/201803/1230705-20180324162402194-473127899.png)
设红色点为关键点,让我们来看看最后的虚树
橙色点是虚树中关键点的lca,虚点和虚边就是被去掉了。
这里看起来是两条边,其实合成了一条边
![](https://images2018.cnblogs.com/blog/1230705/201803/1230705-20180324161102173-1191227162.png)
最后输出虚树根节点(root)即可。
然后我们还发现一个性质就是一个点是关键点,那么它的儿子结点可以直接忽略。
那么说了这道题怎么做了,就差怎么建出虚树了。
建虚树首先我们dfs一遍原树,建出dfs序。
我们先把关键点按从小到大排序。
然后用一个栈的形式维护一条链。
简单说
如果当前点和栈顶为一条链,那么可以直接加入栈。
如果不为一条链,那么得一直删除栈顶,直到形成一条链。
怎么具体实现呢?
设当前点和栈顶lca为g
那么如果dfn[g] >= dfn[top]那么很显然是一条链,直接push当前点即可
如果dfn[g] < dfn[top]那么不是一条链,边pop,边连边栈顶第二个个元素和栈顶。
知道dfn[g] >= dfn[top - 1],那么把top - 1 和 top连边后加入g即可。
最后一条链没有点去pop它,所以最后还要特殊判断栈内是否还要有一条链。
这样就很方便了。
AC代码:
# include <iostream> # include <cstdio> # include <cstring> # include <algorithm> using namespace std; typedef long long LL; const int N = 6e5 + 12; const LL inf = 1e15; int hson[N],sz[N],dep[N],top[N],id[N],fa[N];LL f[N]; int head[N],dt,tot,que[N],n,m,k,a[N],cnt;LL w[N]; struct Edge{ int to,nex;LL w; }edge[N << 1]; void AddEdge(int u,int v,LL w) { if(u == v)return; edge[++dt] = (Edge){v,head[u],w}; head[u] = dt; } void dfs(int u) { sz[u] = 1; for(int i = head[u];i;i = edge[i].nex) { if(sz[edge[i].to])continue; dep[edge[i].to] = dep[u] + 1; fa[edge[i].to] = u; w[edge[i].to] = min(w[u],edge[i].w); dfs(edge[i].to); sz[u] += sz[edge[i].to]; if(sz[hson[u]] < sz[edge[i].to])hson[u] = edge[i].to; } } void dfs(int u,int tp) { top[u] = tp;id[u] = ++tot; if(hson[u])dfs(hson[u],tp); for(int i = head[u];i;i = edge[i].nex) if(!id[edge[i].to])dfs(edge[i].to,edge[i].to); head[u] = 0; } int lca(int u,int v) { while(top[u] != top[v]) { if(dep[top[u]] < dep[top[v]])swap(u,v); u = fa[top[u]]; } return dep[u] < dep[v] ? u : v; } bool cmp(int x,int y){return id[x] < id[y];} void dp(int u) { LL val = 0;f[u] = w[u]; for(int i = head[u];i;i = edge[i].nex) dp(edge[i].to),val += f[edge[i].to]; if(val)f[u] = val < f[u] ? val : f[u]; head[u] = 0; } void solve() { scanf("%d",&k);cnt = 1;int g,top;dt = 0; for(int i = 1;i <= k;i++)scanf("%d",&a[i]); sort(a + 1,a + k + 1,cmp); for(int i = 2;i <= k;i++)if(lca(a[i],a[cnt]) != a[cnt])a[++cnt] = a[i]; que[top = 1] = 1; for(int i = 1;i <= cnt;i++) { g = lca(a[i],que[top]); while(1) { if(dep[que[top - 1]] <= dep[g]) { AddEdge(g,que[top],0);top--; if(que[top] != g)que[++top] = g; break; } AddEdge(que[top - 1],que[top],0);top--; } if(que[top] != a[i])que[++top] = a[i]; } top--; while(top)AddEdge(que[top],que[top + 1],0),top--; dp(1); printf("%lld\n",f[1]); } int main() { scanf("%d",&n);int x,y;LL z;w[1] = inf; for(int i = 1;i < n;i++) { scanf("%d %d %lld",&x,&y,&z); AddEdge(x,y,z);AddEdge(y,x,z); } dfs(1);dfs(1,1); scanf("%d",&m); while(m--)solve(); }