BZOJ2286: [Sdoi2011]消耗战
题解:
又是一个大坑。。。
算法一:很明显,最小割。预计得分20分。
算法二:每次做一遍树DP,预计得分40分。
算法三:虚树+单调栈。
算法三是为了优化算法二而出现的,因为关键点很少,而我们每次都要dfs整棵树过于浪费。
而我们只把关键点和它们的lca拎出来。使得每个点要不是关键点,要不是lca用来汇总答案。
因为其它点只是某条链上的中间点,完全没必要。
具体实现可以按dfs序排序,然后求lca,往栈里压,没用的就弹出。
这里说了一句话很好:lca的单调性。这保证了前面已经弹出的节点及lca不会再次出现。
代码:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<iostream> 7 #include<vector> 8 #include<map> 9 #include<set> 10 #include<queue> 11 #include<string> 12 #define inf 1000000000000LL 13 #define maxn 250000+5 14 #define maxm 100000+5 15 #define eps 1e-10 16 #define ll long long 17 #define pa pair<int,int> 18 #define for0(i,n) for(int i=0;i<=(n);i++) 19 #define for1(i,n) for(int i=1;i<=(n);i++) 20 #define for2(i,x,y) for(int i=(x);i<=(y);i++) 21 #define for3(i,x,y) for(int i=(x);i>=(y);i--) 22 #define for4(i,x) for(int i=head[x],y=e[i].go;i;i=e[i].next,y=e[i].go) 23 #define mod 1000000007 24 using namespace std; 25 inline int read() 26 { 27 int x=0,f=1;char ch=getchar(); 28 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 29 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 30 return x*f; 31 } 32 int n,m,id[maxn],cnt,dep[maxn],f[maxn][19],g[maxn][19]; 33 ll dp[maxn]; 34 bool v[maxn]; 35 inline int lca(int x,int y) 36 { 37 if(dep[x]<dep[y])swap(x,y); 38 int t=dep[x]-dep[y]; 39 for0(i,18)if(t&(1<<i))x=f[x][i]; 40 if(x==y)return x; 41 for3(i,18,0)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; 42 return f[x][0]; 43 } 44 inline int dist(int x,int y) 45 { 46 int t=dep[x]-dep[y],ret=1000000000; 47 for3(i,18,0)if(t&(1<<i))ret=min(ret,g[x][i]),x=f[x][i]; 48 return ret; 49 } 50 struct graph 51 { 52 int head[maxn],tot; 53 struct edge{int go,next,w;}e[2*maxn]; 54 inline void add(int x,int y,int w) 55 { 56 e[++tot]=(edge){y,head[x],w};head[x]=tot; 57 e[++tot]=(edge){x,head[y],w};head[y]=tot; 58 } 59 inline void add(int x,int y) 60 { 61 e[++tot]=(edge){y,head[x],0};head[x]=tot; 62 } 63 inline void dfs(int x) 64 { 65 id[x]=++cnt; 66 for1(i,20)if(dep[x]>=1<<i)f[x][i]=f[f[x][i-1]][i-1],g[x][i]=min(g[x][i-1],g[f[x][i-1]][i-1]); 67 else break; 68 for4(i,x)if(!dep[y]) 69 { 70 dep[y]=dep[x]+1;f[y][0]=x;g[y][0]=e[i].w; 71 dfs(y); 72 } 73 } 74 inline void dfs(int x,int fa) 75 { 76 dp[x]=0; 77 for4(i,x)if(y!=fa) 78 { 79 dfs(y,x); 80 dp[x]+=min(v[y]?inf:dp[y],(ll)dist(y,x)); 81 } 82 head[x]=0; 83 } 84 }G1,G2; 85 int a[maxn],sta[maxn],top; 86 inline bool cmp(int x,int y){return id[x]<id[y];} 87 int main() 88 { 89 freopen("input.txt","r",stdin); 90 freopen("output.txt","w",stdout); 91 n=read(); 92 for1(i,n-1){int x=read(),y=read();G1.add(x,y,read());} 93 dep[1]=1; 94 G1.dfs(1); 95 int T=read(); 96 while(T--) 97 { 98 m=read(); 99 for1(i,m)a[i]=read(); 100 sort(a+1,a+m+1,cmp); 101 for1(i,m)v[a[i]]=1; 102 sta[top=1]=1;G2.tot=0; 103 for1(i,m) 104 { 105 int x=a[i],f=lca(x,sta[top]); 106 while(dep[f]<dep[sta[top]]) 107 { 108 if(dep[f]>=dep[sta[top-1]]) 109 { 110 G2.add(f,sta[top--]); 111 if(sta[top]!=f)sta[++top]=f; 112 break; 113 } 114 G2.add(sta[top-1],sta[top]);top--; 115 } 116 if(sta[top]!=x)sta[++top]=x; 117 } 118 while(--top)G2.add(sta[top],sta[top+1]); 119 G2.dfs(1,0); 120 printf("%lld\n",dp[1]); 121 for1(i,m)v[a[i]]=0; 122 } 123 return 0; 124 }
2286: [Sdoi2011]消耗战
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 520 Solved: 140
[Submit][Status]
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行,分别代表每次任务的最小代价。