[SDOI2011][BZOJ2286] 消耗战|虚树|树型dp|树上倍增LCA
2286: [Sdoi2011]消耗战
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1040 Solved: 363
[Submit][Status][Discuss]
Description
在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。
侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。
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
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
【数据规模和约定】
对于10%的数据,2<=n<=10,1<=m<=5,1<=ki<=n-1
对于20%的数据,2<=n<=100,1<=m<=100,1<=ki<=min(10,n-1)
对于40%的数据,2<=n<=1000,m>=1,sigma(ki)<=500000,1<=ki<=min(15,n-1)
对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1
HINT
Source
又练习了一遍虚树。
大体思路一样,不过yy以下,把普通的虚树也缩掉链(预处理dfs序排序后相邻点的lca,如果属于一条链则把深度大的点删掉)
一开始极大值赋小了……唉。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<vector> #define N 250005 #define ll long long using namespace std; int n,m,q,cnt,dfn,top; ll f[N],mn[N]; int a[N],s[N],deep[N],head[N],id[N],fa[N][21]; int next[2*N],list[2*N],key[2*N]; inline int read() { int a=0,f=1; char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();} while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();} return a*f; } inline bool cmp(int a,int b) { return id[a]<id[b]; } inline void insert(int x,int y,int z) { next[++cnt]=head[x]; head[x]=cnt; list[cnt]=y; key[cnt]=z; } inline void insert0(int x,int y) { if (x==y) return; next[++cnt]=head[x]; head[x]=cnt; list[cnt]=y; } void dfs(int x) { id[x]=++dfn; for (int i=1;(1<<i)<=deep[x];i++) fa[x][i]=fa[fa[x][i-1]][i-1]; for (int i=head[x];i;i=next[i]) { if (list[i]==fa[x][0]) continue; mn[list[i]]=min(mn[x],(ll)key[i]); fa[list[i]][0]=x; deep[list[i]]=deep[x]+1; dfs(list[i]); } } inline int lca(int x,int y) { if (deep[x]>deep[y]) swap(x,y); int t=deep[y]-deep[x]; for (int i=0;(1<<i)<=t;i++) if ((1<<i)&t) y=fa[y][i]; for (int i=18;i>=0;i--) if (fa[x][i]!=fa[y][i]) {x=fa[x][i]; y=fa[y][i];} return x==y?x:fa[x][0]; } void dp(int x) { f[x]=mn[x]; ll tmp=0; for (int i=head[x];i;i=next[i]) { dp(list[i]); tmp+=f[list[i]]; } head[x]=0; if (tmp&&tmp<f[x]) f[x]=tmp; } inline void query() { cnt=top=0; int m=read(); for (int i=1;i<=m;i++) a[i]=read(); sort(a+1,a+m+1,cmp); int tot=0; a[++tot]=a[1]; for(int i=2;i<=m;i++) if(lca(a[tot],a[i])!=a[tot])a[++tot]=a[i]; s[++top]=1; for (int i=1;i<=tot;i++) { int t=a[i],f=0; while (top>0) { f=lca(s[top],t); if (top>1&&deep[f]<deep[s[top-1]]) {insert0(s[top-1],s[top]); top--;} else if (deep[f]<deep[s[top]]) {insert0(f,s[top]); top--; break;} else break; } if (s[top]!=f) s[++top]=f; s[++top]=t; } while (top>1) {insert0(s[top-1],s[top]); top--;} dp(1); printf("%lld\n",f[1]); } int main() { n=read(); for (int i=1;i<n;i++) { int u=read(),v=read(),w=read(); insert(u,v,w); insert(v,u,w); } mn[1]=10000000000000; dfs(1); q=read(); memset(head,0,sizeof(head)); while (q--) query(); return 0; }