BZOJ 2286 消耗战
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
首先很明显是树形dp,开始我yy了一个暴力dp,f[i][0]表示以i为根的子树完全与能源点隔断的最小代价,f[i][1]表示以i为根的子树没有完全与能源点隔断的最小代价。但后来发现其实完全没有必要这么做。
令best[i]表示从i到根的边权的最小值,f[i]表示切断i的子树的最小代价。转移:f[i]=min(best[i],Σf[son])。
但是裸的dp的话肯定是会TLE的。我们仔细想想,每次询问都有许多冗余的点来浪费复杂度,所以我们可以只用询问点及他们的LCA来建一颗新树,我们暂且称其为虚树,然后在虚树上跑dp,效率就会高很多(当然,去除了很多没有用的点嘛)。
那么问题就来了,怎么建虚树呢???我们利用的是单调栈。
将所有询问点按照dfs序排一遍序,然后,一次加入栈中(栈中初始化为节点1)。若枚举的节点h[i]与栈顶元素的s[top] 的LCA fa的深度比栈顶元素深度小,s[top-1]向s[top]连接一条边,栈顶再见,fa与h[i]都加入栈中。就这样,一颗虚树建立出来了。(具体实现见代码)
1 #include<cstring> 2 #include<iostream> 3 #include<cstdio> 4 #include<algorithm> 5 #include<cstdlib> 6 using namespace std; 7 8 typedef long long ll; 9 #define maxn (250010) 10 #define inf (1LL<<60) 11 int n,m,cnt,side[maxn],next[maxn*2],dep[maxn],h[maxn],st[maxn],sign[maxn]; 12 int toit[maxn*2],cost[maxn*2],f[maxn][30],id[maxn],ID; 13 ll g[maxn],best[maxn]; 14 15 inline int read() 16 { 17 int x=0,f=1;char ch=getchar(); 18 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 19 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 20 return x*f; 21 } 22 23 inline bool cmp(int a,int b) { return id[a] < id[b]; } 24 25 inline void add(int a,int b,int c) 26 { 27 next[++cnt] = side[a]; side[a] = cnt; 28 toit[cnt] = b; cost[cnt] = c; 29 } 30 31 inline void ins(int a,int b,int d) 32 { 33 if (a == b) return; 34 if (sign[a] != d) side[a] = 0,sign[a] = d; 35 if (sign[b] != d) side[b] = 0,sign[b] = d; 36 next[++cnt] = side[a]; side[a] = cnt; toit[cnt] = b; 37 } 38 39 inline void dfs(int now) 40 { 41 id[now] = ++ID; 42 for (int i = 1;1 << i <= dep[now];++i) 43 f[now][i] = f[f[now][i-1]][i-1]; 44 for (int i = side[now];i;i = next[i]) 45 if (toit[i] != f[now][0]) 46 { 47 best[toit[i]] = min(best[now],(ll)cost[i]); 48 dep[toit[i]] = dep[now] + 1; 49 f[toit[i]][0] = now; 50 dfs(toit[i]); 51 } 52 } 53 54 inline void jump(int &a,int step) 55 { 56 int i = 0; 57 for (;step;step >>= 1,++i) if (step&1) a = f[a][i]; 58 } 59 60 inline int lca(int a,int b) 61 { 62 if (dep[a]<dep[b]) swap(a,b); 63 jump(a,dep[a]-dep[b]); 64 if (a == b) return a; 65 for (int i = 0;i >= 0;) 66 { 67 if (f[a][i] != f[b][i]) a = f[a][i],b = f[b][i],++i; 68 else --i; 69 } 70 return f[a][0]; 71 } 72 73 inline void dp(int now) 74 { 75 g[now] = best[now]; 76 ll tmp = 0; 77 for (int i = side[now];i;i = next[i]) 78 dp(toit[i]),tmp += g[toit[i]]; 79 if (tmp && tmp <= g[now]) g[now] = tmp; 80 } 81 82 inline void work(int p) 83 { 84 cnt = 0; 85 int K = read(),tot,top; 86 for (int i = 1;i <= K;++i) h[i] = read(); 87 sort(h+1,h+K+1,cmp); 88 h[tot = 1] = h[1]; 89 for (int i = 2;i <= K;++i) if (lca(h[tot],h[i]) != h[tot]) h[++tot] = h[i]; 90 st[top = 1] = 1; 91 for (int i = 1;i <= tot;++i) 92 { 93 int ans = lca(h[i],st[top]); 94 while (true) 95 { 96 if (dep[ans] >= dep[st[top-1]]) 97 { 98 ins(ans,st[top--],p); 99 break; 100 } 101 ins(st[top-1],st[top],p); --top; 102 } 103 if (st[top] != ans) st[++top] = ans; 104 if (st[top] != h[i]) st[++top] = h[i]; 105 } 106 while (--top) ins(st[top],st[top+1],p); 107 dp(1); 108 printf("%lld\n",g[1]); 109 } 110 111 int main() 112 { 113 freopen("2286.in","r",stdin); 114 freopen("2286.out","w",stdout); 115 scanf("%d",&n); 116 for (int i = 1;i < n;++i) 117 { 118 int a = read(),b = read(),c = read(); 119 add(a,b,c); add(b,a,c); 120 } 121 dep[0] = -1; best[1] = inf; dfs(1); 122 scanf("%d",&m); 123 for (int i = 1;i <= m;++i) work(i); 124 fclose(stdin); fclose(stdout); 125 return 0; 126 }
如果觉得我的代码和hzwer的很像,你就假设长得不像吧。
高考结束,重新回归。