【BZOJ2286】消耗战(虚树,DFS序,树形DP)
题意:一棵N个点的树上有若干个关键点,每条边有一个边权,现在要将这些关键点到1的路径全部切断,切断一条边的代价就是边权。
共有M组询问,每组询问有k[i]个关键点,对于每组询问求出完成任务的最小代价。
对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1
思路:第一题虚树,需要详细地记录一下。
对于此题,朴素的树形DP很好理解:
dp[u]为将u子树中的关键点全部切断的最小代价
dp[u]=min(cut[u],sigma(dp[v])) 其中cut[u]为1到u中最小的边权
但因为询问有多次且需要切断的关键点不同,会超时
经过思考可以发现只有很少的点需要被作为关键点进行处理:当一个点是关键点,或者为某两个关键点的LCA时,这个点会被作为关键点
显然关键点的数量是O(n)级别的
对于每次询问重新构造一棵虚树,使用栈
现将关键点按DFS序从小到大排序
对于一条链上的点只需保留两个
设新插入的点为x,栈顶的第一个点为y,第二个点为z,x与y的LCA为w:
1.dfn[w]<dfn[z] (w,x)连边,x入栈
2.dfn[w]=dfn[z] (y,z)连边
3.dfn[w]>dfn[z] 将w加入栈,(w,z),(w,x)之间连边
1 const oo=1000000000000; 2 var head,vet,next,len,head1,vet1,next1, 3 dep,flag,dfn,b,h,stk:array[0..500000]of longint; 4 dp,cut:array[1..500000]of int64; 5 f:array[1..500000,0..20]of longint; 6 n,i,x,y,z,tot,time,que:longint; 7 8 procedure add(a,b,c:longint); 9 begin 10 inc(tot); 11 next[tot]:=head[a]; 12 vet[tot]:=b; 13 len[tot]:=c; 14 head[a]:=tot; 15 end; 16 17 function min(x,y:int64):int64; 18 begin 19 if x<y then exit(x); 20 exit(y); 21 end; 22 23 procedure swap(var x,y:longint); 24 var t:longint; 25 begin 26 t:=x; x:=y; y:=t; 27 end; 28 29 function lca(x,y:longint):longint; 30 var i,d:longint; 31 begin 32 if dep[x]<dep[y] then swap(x,y); 33 d:=dep[x]-dep[y]; 34 for i:=0 to 20 do 35 if d and (1<<i)>0 then x:=f[x,i]; 36 for i:=20 downto 0 do 37 if f[x,i]<>f[y,i] then 38 begin 39 x:=f[x,i]; y:=f[y,i]; 40 end; 41 if x=y then exit(x); 42 exit(f[x,0]); 43 end; 44 45 procedure dfs(u:longint); 46 var e,i,v:longint; 47 begin 48 for i:=1 to 20 do 49 begin 50 if dep[u]<(1<<i) then break; 51 f[u,i]:=f[f[u,i-1],i-1]; 52 end; 53 inc(time); dfn[u]:=time; 54 flag[u]:=1; 55 e:=head[u]; 56 while e<>0 do 57 begin 58 v:=vet[e]; 59 if flag[v]=0 then 60 begin 61 f[v,0]:=u; 62 dep[v]:=dep[u]+1; 63 cut[v]:=min(cut[u],len[e]); 64 dfs(v); 65 end; 66 e:=next[e]; 67 end; 68 end; 69 70 procedure qsort(l,r:longint); 71 var i,j,mid:longint; 72 begin 73 i:=l; j:=r; mid:=b[(l+r)>>1]; 74 repeat 75 while mid>b[i] do inc(i); 76 while mid<b[j] do dec(j); 77 if i<=j then 78 begin 79 swap(h[i],h[j]); 80 swap(b[i],b[j]); 81 inc(i); dec(j); 82 end; 83 until i>j; 84 if l<j then qsort(l,j); 85 if i<r then qsort(i,r); 86 end; 87 88 procedure add1(a,b:longint); 89 begin 90 if a=b then exit; 91 // writeln(a,' ',b); 92 inc(tot); 93 next1[tot]:=head1[a]; 94 vet1[tot]:=b; 95 head1[a]:=tot; 96 end; 97 98 procedure dfs2(u:longint); 99 var e,v:longint; 100 s:int64; 101 begin 102 e:=head1[u]; dp[u]:=cut[u]; 103 s:=0; 104 while e<>0 do 105 begin 106 v:=vet1[e]; 107 dfs2(v); 108 s:=s+dp[v]; 109 e:=next1[e]; 110 end; 111 head1[u]:=0; 112 if s=0 then dp[u]:=cut[u] 113 else dp[u]:=min(s,dp[u]); 114 end; 115 116 procedure solve; 117 var m,i,top,now,p,q:longint; 118 begin 119 read(m); tot:=0; 120 for i:=1 to m do begin read(h[i]); b[i]:=dfn[h[i]]; end; 121 qsort(1,m); 122 q:=1; 123 for i:=2 to m do 124 if lca(h[q],h[i])<>h[q] then begin inc(q); h[q]:=h[i]; end; 125 //for i:=1 to q do writeln(h[i]); 126 stk[1]:=1; top:=1; 127 for i:=1 to q do 128 begin 129 now:=h[i]; p:=lca(now,stk[top]); 130 while true do 131 begin 132 if dep[p]>=dep[stk[top-1]] then 133 begin 134 add1(p,stk[top]); dec(top); 135 if stk[top]<>p then begin inc(top); stk[top]:=p; end; 136 break; 137 end; 138 add1(stk[top-1],stk[top]); dec(top); 139 end; 140 if stk[top]<>now then begin inc(top); stk[top]:=now; end; 141 end; 142 repeat 143 dec(top); 144 if top<=0 then break; 145 add1(stk[top],stk[top+1]); 146 until top=0; 147 dfs2(1); 148 writeln(dp[1]); 149 end; 150 151 begin 152 assign(input,'bzoj2286.in'); reset(input); 153 assign(output,'bzoj2286.out'); rewrite(output); 154 readln(n); 155 for i:=1 to n-1 do 156 begin 157 readln(x,y,z); 158 add(x,y,z); 159 add(y,x,z); 160 end; 161 readln(que); 162 //fillchar(cut,sizeof(cut),$1f); 163 cut[1]:=oo; 164 dfs(1); 165 for i:=1 to que do solve; 166 close(input); 167 close(output); 168 end.
null