首页 > 代码库 > 【BZOJ2286】消耗战(虚树,DFS序,树形DP)

【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.

 

【BZOJ2286】消耗战(虚树,DFS序,树形DP)