[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
如果是单次询问,可以$O(n)$的树dp做出来
这题询问数很多,但$k$的和不大
对于每次询问,我们建一棵“虚树”
这棵虚树只包括询问点以及相应的lca
这样在虚树上dp复杂度为$O(k)$
总复杂度为$O(\sumk)$
树dp可以这样Min[x]表示1~x路径上最小的边
f[x]表示1无法到达x子树中关键点的最小和
f[x]=min(Min[x],∑f[v])
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 typedef long long lol; 8 const int N=1000005; 9 struct Node 10 { 11 int next,to; 12 lol dis; 13 }edge[N],edge2[N]; 14 int head[N],head2[N],num,dep[N],a[N],s[N],cnt,dfn[N]; 15 int fa[N][25],vis[N],n,top,bin[21],ed[N],M; 16 lol Min[N]; 17 int gi() 18 { 19 char ch=getchar(); 20 int x=0; 21 while (ch<'0'||ch>'9') ch=getchar(); 22 while (ch>='0'&&ch<='9') 23 { 24 x=x*10+ch-'0'; 25 ch=getchar(); 26 } 27 return x; 28 } 29 bool cmp(int a,int b) 30 { 31 return dfn[a]<dfn[b]; 32 } 33 void add(int u,int v,lol w) 34 { 35 num++; 36 edge[num].next=head[u]; 37 head[u]=num; 38 edge[num].to=v; 39 edge[num].dis=w; 40 } 41 void dfs(int x,int pa) 42 {int i; 43 dep[x]=dep[pa]+1; 44 dfn[x]=++cnt; 45 for (i=1;bin[i]<=dep[x];i++) 46 fa[x][i]=fa[fa[x][i-1]][i-1]; 47 for (i=head[x];i;i=edge[i].next) 48 { 49 int v=edge[i].to; 50 if (v==pa) continue; 51 Min[v]=min(Min[x],edge[i].dis); 52 fa[v][0]=x; 53 dfs(v,x); 54 } 55 ed[x]=cnt; 56 } 57 int lca(int x,int y) 58 {int as,i; 59 if (dep[x]<dep[y]) swap(x,y); 60 for (i=20;i>=0;i--) 61 if (bin[i]<=dep[x]-dep[y]) 62 x=fa[x][i]; 63 if (x==y) return x; 64 for (i=20;i>=0;i--) 65 { 66 if (fa[x][i]!=fa[y][i]) 67 { 68 x=fa[x][i];y=fa[y][i]; 69 } 70 } 71 return fa[x][0]; 72 } 73 void add2(int u,int v) 74 { 75 if (u==v) return; 76 num++; 77 edge2[num].next=head2[u]; 78 head2[u]=num; 79 edge2[num].to=v; 80 } 81 lol dp(int x) 82 {int i; 83 if (vis[x]) 84 return Min[x]; 85 lol tmp=0; 86 for (i=head2[x];i;i=edge2[i].next) 87 { 88 int v=edge2[i].to; 89 tmp+=dp(v); 90 } 91 head2[x]=0; 92 return min(tmp,Min[x]); 93 } 94 int main() 95 {int i,u,v,j,q,k; 96 lol w; 97 cin>>n; 98 bin[0]=1; 99 for (i=1;i<=20;i++) 100 bin[i]=bin[i-1]*2; 101 for (i=1;i<=n-1;i++) 102 { 103 scanf("%d%d%lld",&u,&v,&w); 104 add(u,v,w);add(v,u,w); 105 } 106 Min[1]=2e15; 107 dfs(1,0); 108 cin>>q; 109 while (q--) 110 { 111 k=gi(); 112 M=k; 113 num=0; 114 for (i=1;i<=k;i++) 115 a[i]=gi(),vis[a[i]]=1; 116 sort(a+1,a+k+1,cmp); 117 for (i=2;i<=k;i++) 118 if (ed[a[i-1]]<dfn[a[i]]) 119 a[++M]=lca(a[i-1],a[i]);a[++M]=1; 120 sort(a+1,a+M+1,cmp); 121 M=unique(a+1,a+M+1)-a-1; 122 s[++top]=a[1]; 123 for (i=2;i<=M;i++) 124 { 125 while (top&&ed[s[top]]<dfn[a[i]]) top--; 126 add2(s[top],a[i]); 127 s[++top]=a[i]; 128 } 129 printf("%lld\n",dp(1)); 130 for (i=1;i<=M;i++) 131 vis[a[i]]=head2[a[i]]=0; 132 } 133 }