BZOJ2286 [Sdoi2011]消耗战

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

Sample Output

12
32
22

HINT

对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1

正解:虚树+树形DP

解题报告:

  听说这是虚树裸题,于是我跑过来AC了。

  考虑直接树形DP,点数过多,而且大多数没有用,会超时。那我们想保留一些必要的点,而去掉无用的点。

  首先虚树的构建也是一个很神的算法。网上代码很多,但是总结还是少。

  遥遥的题解,传送门:http://user.qzone.qq.com/872191552/blog/1464431226

  考虑维护一个栈,每次往里面插新结点,然后特判一下二者的lca和栈顶元素的情况。具体的看遥遥的吧,懒得说了,等切世界树的时候再写一份详细的吧。

  1 //It is made by jump~
  2 #include <iostream>
  3 #include <cstdlib>
  4 #include <cstring>
  5 #include <cstdio>
  6 #include <cmath>
  7 #include <algorithm>
  8 #include <ctime>
  9 #include <vector>
 10 #include <queue>
 11 #include <map>
 12 #include <set>
 13 #ifdef WIN32
 14 #define OT "%I64d"
 15 #else
 16 #define OT "%lld"
 17 #endif
 18 using namespace std;
 19 typedef long long LL;
 20 const int MAXN = 250011;
 21 LL inf;//inf不能开小了
 22 int n,m,ecnt,tot,id[MAXN],k;
 23 int first[MAXN],next[MAXN*2],to[MAXN*2],w[MAXN*2];
 24 LL val[MAXN];//这个点到根上的最小边权
 25 int jump[MAXN][19],deep[MAXN];
 26 int que[MAXN],top,Stack[MAXN];
 27 int head[MAXN];
 28 LL f[MAXN];
 29
 30 inline int getint()
 31 {
 32        int w=0,q=0;
 33        char c=getchar();
 34        while((c<‘0‘ || c>‘9‘) && c!=‘-‘) c=getchar();
 35        if (c==‘-‘)  q=1, c=getchar();
 36        while (c>=‘0‘ && c<=‘9‘) w=w*10+c-‘0‘, c=getchar();
 37        return q ? -w : w;
 38 }
 39
 40 struct edge{
 41     int to,next;
 42 }e[MAXN];
 43
 44 inline LL min(LL x,LL y){ if(x<y) return x;  return y; }
 45
 46 inline void dfs(int x,int fa){
 47     jump[x][0]=fa; id[x]=++ecnt;//作出dfs序
 48     for(int i=1;i<=18;i++) jump[x][i]=jump[jump[x][i-1]][i-1];
 49     for(int i=first[x];i;i=next[i]) {
 50     int v=to[i];
 51     if(v==fa) continue;
 52     val[v]=min(w[i],val[x]); deep[v]=deep[x]+1;
 53     dfs(v,x);
 54     }
 55 }
 56
 57 inline bool cmp(int a,int b){return id[a]<id[b];}
 58
 59 inline int lca(int x,int y){
 60     if(deep[x]<deep[y]) swap(x,y);
 61     int t=0; while((1<<t) <= deep[x]) t++;
 62     t--; for(int i=t;i>=0;i--) if(deep[x]-(1<<i)>=deep[y]) x=jump[x][i];
 63     if(x==y) return y;
 64     for(int i=t;i>=0;i--) if(jump[x][i]!=jump[y][i]) { x=jump[x][i]; y=jump[y][i]; }
 65     return jump[x][0];
 66 }
 67
 68 inline void link(int x,int y){ if(x==y) return ; e[++ecnt].next=head[x]; head[x]=ecnt; e[ecnt].to=y;}
 69
 70 inline void dp(int x){
 71     LL lin=0; f[x]=val[x];
 72     for(int i=head[x];i;i=e[i].next) {
 73     dp(e[i].to);
 74     lin+=f[e[i].to];
 75     }
 76     head[x]=0;//退出的时候顺便清空
 77     if(!lin) f[x]=val[x];
 78     else if(lin<f[x]) f[x]=lin;
 79 }
 80
 81 inline void solve(){//断绝到根结点1的路径
 82     m=getint(); for(int i=1;i<=m;i++) que[i]=getint();
 83     sort(que+1,que+m+1,cmp);//按dfs序排序
 84     tot=0; que[++tot]=que[1];
 85     for(int i=2;i<=m;i++) if(lca(que[i],que[tot])!=que[tot]) que[++tot]=que[i];//应该是和tot比较
 86     //在下面的肯定不用计算,只要切断上部的即可
 87     top=0;Stack[++top]=1; int grand;//最近公共祖先
 88     ecnt=0;
 89     for(int i=1;i<=tot;i++) {//分类讨论
 90     grand=lca(Stack[top],que[i]);
 91     while(1) {
 92         if(deep[Stack[top-1]]<=deep[grand]) {//分别处在两个子树,grand深度更大!!!
 93         link(grand,Stack[top]); top--;
 94         if(Stack[top]!=grand) Stack[++top]=grand;
 95         break;
 96         }
 97         link(Stack[top-1],Stack[top]); top--;
 98     }
 99     if(Stack[top]!=que[i]) Stack[++top]=que[i];//在同一子树
100     }
101     top--;
102     while(top) link(Stack[top],Stack[top+1]),top--;//剩余的记得连上
103     dp(1);
104     printf(OT"\n",f[1]);
105 }
106
107 inline void work(){
108     n=getint(); int x,y,z;
109     inf=1;for(int i=1;i<=60;i++) inf*=2;
110     for(int i=1;i<n;i++) {
111     x=getint(); y=getint(); z=getint();
112     next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z;
113     next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x; w[ecnt]=z;
114     }
115     val[1]=inf; ecnt=0; deep[1]=0; dfs(1,0);
116     k=getint(); for(int i=1;i<=k;i++) solve();
117 }
118
119 int main()
120 {
121   work();
122   return 0;
123 }
时间: 2024-11-16 21:54:33

BZOJ2286 [Sdoi2011]消耗战的相关文章

Bzoj2286 [Sdoi2011消耗战

Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 3003  Solved: 1081 Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿.由于不同桥梁的材质和结构不同,所以炸毁不

[bzoj2286][Sdoi2011]消耗战(虚树上的DP)

题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2286 分析:对于普通的树形dp:f[x]=min(∑f[son],m[x]),其中f[x]表示以x为根的子树所有关键点全部断开所需要的最少代价,m[x]表示从根节点到x节点的路径上最短的边 这样的话复杂度是n*m的,明显TLE 注意到数据范围里提到,所有询问的总的点数<=n,也就是说询问虽然很多,但每次询问的点的个数不多,也就是说如果对所有的点做树形dp就很不值得 于是我们就要取出那

【BZOJ2286】[Sdoi2011]消耗战 虚树

[BZOJ2286][Sdoi2011]消耗战 Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿.由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小. 侦查部门还发现,敌军有

bzoj 2286 [Sdoi2011]消耗战(虚树+树上DP)

2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1276  Solved: 445[Submit][Status][Discuss] Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸

【BZOJ-2286】消耗战 虚树 + 树形DP

2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2120  Solved: 752[Submit][Status][Discuss] Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁

AC日记——[SDOI2011]消耗战 洛谷 P2495

[SDOI2011]消耗战 思路: 建虚树走树形dp: 代码: #include <bits/stdc++.h> using namespace std; #define INF 1e17 #define maxn 250005 #define ll long long #define maxm (maxn<<1) struct LandType { ll id,key; bool operator<(const LandType pos)const { return key

[Sdoi2011]消耗战

2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 3108  Solved: 1115[Submit][Status][Discuss] Description 在 一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经 没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任

luogu P2495 [SDOI2011]消耗战

二次联通门 : luogu P2495 [SDOI2011]消耗战 /* luogu P2495 [SDOI2011]消耗战 虚树+树形dp 首先对原图构建出虚树 在建图的时候处理出最小值 转移即可 小技巧 在dp的过程中可以顺便把边表清空 将边结构体封装可方便的建多张图 */ #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #define INF 1

&lt;虚树+树型DP&gt; SDOI2011消耗战

<虚树+树型DP> SDOI2011消耗战 #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; typedef long long LL; const int MAXN = 25e4 + 10; inline LL in() { LL x = 0, flag