BZOJ2286[Sdoi2011]消耗战 虚树
Posted CQzhangyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ2286[Sdoi2011]消耗战 虚树相关的知识,希望对你有一定的参考价值。
【BZOJ2286】[Sdoi2011]消耗战
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
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
32
22
HINT
对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1
题解:特地学了一下虚树的造法。
我们将每次给出的所有点,以及他们影响到的所有点(即他们中任意两点的LCA)都拿出来,然后跑个树形DP就行了。这些点形成的东西叫虚树,问题是怎么建呢?
本人naive的做法:将给出的点按dfs序排序,然后求出相邻两点的LCA,显然这些LCA就是所有点对的LCA,所以这说明虚树的大小是O(k)的。然后我们再将这些点按dfs序排序,然后从左到右模拟DFS的过程,如果下一个点再当前点的子树中,则递归下去,否则回溯。
好吧下面说更NB的做法:先按dfs序排序,然后用栈维护当前的一条链(铭记维护的是链!),那么新加入一个点i+1时,先求出i和i+1的lca,找到这个lca在链上的位置,然后将lca下面的链上的点都弹栈(弹栈的时候顺便连边),最后将lca和i+1扔到栈中。具体做法可以看代码,当然最好还是画画图理解一下。
#include <cstdio> #include <cstring> #include <iostream> #include <vector> #include <algorithm> using namespace std; const int maxn=250010; typedef long long ll; int n,m,K,cnt,top; int to[maxn<<1],next[maxn<<1],val[maxn<<1],head[maxn],fa[20][maxn],Log[maxn],dep[maxn]; int p1[maxn],p2[maxn],vis[maxn],p[maxn],st[maxn]; ll s[maxn]; vector<int> ch[maxn]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘)f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar(); return ret*f; } inline void add(int a,int b,int c) { to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++; } inline int MN(int a,int b) {return dep[a]<dep[b]?a:b;} void dfs(int x) { p1[x]=++p2[0]; for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[0][x]) dep[to[i]]=dep[x]+1,fa[0][to[i]]=x,s[to[i]]=min(s[x],(ll)val[i]),dfs(to[i]); p2[x]=p2[0]; } inline int lca(int a,int b) { if(dep[a]<dep[b]) swap(a,b); for(int i=Log[dep[a]-dep[b]];i>=0;i--) if(dep[fa[i][a]]>=dep[b]) a=fa[i][a]; if(a==b) return a; for(int i=Log[dep[a]];i>=0;i--) if(fa[i][a]!=fa[i][b]) a=fa[i][a],b=fa[i][b]; return fa[0][a]; } bool cmp(int a,int b) { return p1[a]<p1[b]; } inline void Add(int a,int b) { if(b) ch[a].push_back(b); } ll solve(int x) { ll tmp=0; for(int i=0;i<(int)ch[x].size();i++) tmp+=solve(ch[x][i]); ch[x].clear(); if(!vis[x]) tmp=min(tmp,s[x]); else tmp=s[x]; return tmp; } int main() { n=rd(); int i,j,a,b,c; memset(head,-1,sizeof(head)); for(i=1;i<n;i++) a=rd(),b=rd(),c=rd(),add(a,b,c),add(b,a,c); dep[1]=1,s[1]=1ll<<60,dfs(1); for(i=2;i<=n;i++) Log[i]=Log[i>>1]+1; for(j=1;(1<<j)<=n;j++) for(i=1;i<=n;i++) fa[j][i]=fa[j-1][fa[j-1][i]]; m=rd(); for(i=1;i<=m;i++) { K=rd(); for(j=1;j<=K;j++) p[j]=rd(),vis[p[j]]=1; sort(p+1,p+K+1,cmp); st[top=1]=p[1]; for(j=2;j<=K;j++) { a=p[j],b=lca(st[top],a),c=0; while(top&&dep[st[top]]>dep[b]) Add(st[top],c),c=st[top--]; if(st[top]==b) Add(st[top],c); if(dep[st[top]]<dep[b]) Add(b,c),st[++top]=b; st[++top]=a; } while(top>1) Add(st[top-1],st[top]),top--; a=st[1]; printf("%lld\n",solve(a)); for(j=1;j<=K;j++) vis[p[j]]=0; } return 0; }//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
以上是关于BZOJ2286[Sdoi2011]消耗战 虚树的主要内容,如果未能解决你的问题,请参考以下文章