BZOJ2286 [Sdoi2011]消耗战
题目描述
在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。
侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。
输入输出格式
输入格式:
第一行一个整数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,表示资源丰富岛屿的编号。
输出格式:
输出有m行,分别代表每次任务的最小代价。
输入输出样例
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
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
题解:
这个题目,首先我们考虑暴力dp,设dp[i]表示保证子树内部都合法的最小花费,那么显然,dp[i]=sum(dp[to]),dp[i]=min(dp[i],val[i]),val[i]表示i到根节点路径上的最小边权。然而看看数据范围……
想怎么优化,显然每次询问的正真用到的关键点其实是很少的,所以我们可以打一下虚树,这样就可以把询问复杂度降为m*k*logk。于是就套一下虚树模板就可以了(然而我是现学的)。
代码:
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <cmath> #include <iostream> #define MAXN 400100 #define ll long long using namespace std; ll f[MAXN],val[MAXN]; int top[MAXN],size[MAXN],dep[MAXN],son[MAXN],dfn[MAXN],fa[MAXN],b[MAXN]; int s[MAXN*2],q[MAXN]; struct edge{ int first; int next; int to; int quan; }a[MAXN*2],a2[MAXN*2]; int n,m,num=0,num2=0; void addedge(int from,int to,int quan){ a[++num].to=to; a[num].quan=quan; a[num].next=a[from].first; a[from].first=num; } void dfs1(int now,int F){ fa[now]=F,size[now]=1,dep[now]=dep[F]+1; for(int i=a[now].first;i;i=a[i].next){ int to=a[i].to,quan=a[i].quan;if(to==F) continue; val[to]=min((ll)quan,val[now]); dfs1(to,now); size[now]+=size[to]; if(size[to]>size[son[now]]) son[now]=to; } } void dfs2(int now,int tp){ top[now]=tp;dfn[now]=++num2; if(son[now]) dfs2(son[now],tp); for(int i=a[now].first;i;i=a[i].next){ int to=a[i].to; if(to==fa[now]||to==son[now]) continue; dfs2(to,to); } } int Lca(int x,int y){ int topx=top[x],topy=top[y]; while(topx!=topy){ if(dep[topx]<dep[topy]) swap(topx,topy),swap(x,y); x=fa[topx]; topx=top[x]; } if(dep[x]<dep[y]) return x; return y; } bool cmp(int x,int y){ return dfn[x]<dfn[y]; } void addedge2(int from,int to,int quan){ a2[++num].to=to; a2[num].quan=quan; a2[num].next=a2[from].first; a2[from].first=num; } void DP(int now,int FA){ if(b[now]) f[now]=val[now]; else f[now]=0; for(int i=a2[now].first;i;i=a2[i].next){ int to=a2[i].to; if(to==FA||to==now) continue; DP(to,now); f[now]+=f[to]; } f[now]=min(f[now],val[now]); b[now]=a2[now].first=0; } void work(){ int tot,tp=0; scanf("%d",&tot); for(int i=1;i<=tot;i++) scanf("%d",&q[i]),b[q[i]]=1; sort(q+1,q+tot+1,cmp); num=0; s[++tp]=1; for(int i=1;i<=tot;i++){ int lca=Lca(q[i],s[tp]); while(1){ if(dep[s[tp-1]]<=dep[lca]){ addedge2(s[tp],lca,0),addedge2(lca,s[tp],0);tp--; if(s[tp]!=lca) s[++tp]=lca; break; } addedge2(s[tp],s[tp-1],0),addedge2(s[tp-1],s[tp],0);tp--; } s[++tp]=q[i]; } while(tp>1) {addedge2(s[tp],s[tp-1],0),addedge2(s[tp-1],s[tp],0);tp--;} DP(1,0); printf("%lld\n",f[1]); } int main() { scanf("%d",&n); for(int i=1;i<=n-1;i++){ int x,y,z;scanf("%d%d%d",&x,&y,&z); addedge(x,y,z),addedge(y,x,z); } val[1]=1ll<<60; dfs1(1,0);dfs2(1,1); scanf("%d",&m); while(m--) work(); return 0; }