bzoj2286

很明显,20%=mincut 40%=每次暴力树形dp
那么正解是什么呢?不难发现∑ki<=500000,也就是每次询问的复杂度都要跟k有关
从树形dp工作的角度来看,确实有很多点我们根本就没必要访问,我们设每次要割断的岛屿为关键点
这里引入了一个新的东西叫做虚树,简单来说就是把树上没用的点去掉收缩成一棵树
具体来说,如果一个点的子树中至多只有一棵子树包含关键点,那么这个点就可以删掉
也就是说只有关键点和他们的lca会被保留下来就行了,证明总点数<=2*k-1
那么怎么构建虚树?我们先按dfs序排序,然后按这个顺序不断加点维护虚树上最新的路径
感觉建树的方法只可意会不可言传,具体见代码吧……

  1 type node=record
  2        len,po,next:longint;
  3      end;
  4 
  5 var e:array[0..500010] of node;
  6     anc:array[0..250010,0..20] of longint;
  7     b,d,p,c,fa,st,a:array[0..250010] of longint;
  8     f:array[0..250010] of int64;
  9     v:array[0..250010] of boolean;
 10     w,s,m,h,t,n,len,i,x,y,z:longint;
 11 
 12 function min(a,b:longint):longint;
 13   begin
 14     if a>b then exit(b) else exit(a);
 15   end;
 16 
 17 procedure swap(var a,b:longint);
 18   var c:longint;
 19   begin
 20     c:=a;
 21     a:=b;
 22     b:=c;
 23   end;
 24 
 25 procedure sort(l,r: longint);
 26   var i,j,x:longint;
 27   begin
 28     i:=l;
 29     j:=r;
 30     x:=b[a[(l+r) shr 1]];
 31     repeat
 32       while b[a[i]]<x do inc(i);
 33       while x<b[a[j]] do dec(j);
 34       if not(i>j) then
 35       begin
 36         swap(a[i],a[j]);
 37         inc(i);
 38         j:=j-1;
 39       end;
 40     until i>j;
 41     if l<j then sort(l,j);
 42     if i<r then sort(i,r);
 43   end;
 44 
 45 procedure add(x,y,z:longint);
 46   begin
 47     inc(len);
 48     e[len].po:=y;
 49     e[len].len:=z;
 50     e[len].next:=p[x];
 51     p[x]:=len;
 52   end;
 53 
 54 procedure dfs(x:longint);
 55   var i,y:longint;
 56   begin
 57     inc(t);
 58     b[x]:=t;  //dfs序
 59     for i:=1 to h do
 60     begin
 61       y:=anc[x,i-1];
 62       if y<>0 then anc[x,i]:=anc[y,i-1]
 63       else break;
 64     end;
 65     i:=p[x];
 66     while i<>0 do
 67     begin
 68       y:=e[i].po;
 69       if fa[x]<>y then
 70       begin
 71         fa[y]:=x;
 72         anc[y,0]:=x;
 73         c[y]:=min(c[x],e[i].len);  //c[]表示把这个点和根节点割开最小代价
 74         d[y]:=d[x]+1;
 75         dfs(y);
 76       end;
 77       i:=e[i].next;
 78     end;
 79   end;
 80 
 81 function lca(x,y:longint):longint;
 82   var i,p:longint;
 83   begin
 84     if d[x]<d[y] then swap(x,y);
 85     p:=trunc(ln(d[x])/ln(2));
 86     if d[x]>d[y] then
 87       for i:=p downto 0 do
 88         if d[x]-1 shl i>=d[y] then x:=anc[x,i];
 89     if x=y then exit(x);
 90     for i:=p downto 0 do
 91       if (anc[y,i]<>anc[x,i]) then
 92       begin
 93         x:=anc[x,i];
 94         y:=anc[y,i];
 95       end;
 96     exit(fa[x]);
 97   end;
 98 
 99 procedure dp(x:longint);  //dp过程很简单
100   var i,y:longint;
101       s:int64;
102   begin
103     if x=1 then f[1]:=2147483647*2147483647
104     else f[x]:=c[x];
105     i:=p[x];
106     s:=0;
107     while i<>0 do
108     begin
109       y:=e[i].po;
110       dp(y);
111       s:=s+f[y];
112       i:=e[i].next;
113     end;
114     p[x]:=0;
115     if (s<>0) then
116       if f[x]>s then f[x]:=s;
117   end;
118 
119 begin
120   readln(n);
121   h:=trunc(ln(n)/ln(2));
122   for i:=1 to n-1 do
123   begin
124     readln(x,y,z);
125     add(x,y,z);
126     add(y,x,z);
127   end;
128   c[1]:=2147483647;
129   dfs(1);
130   readln(m);
131   fillchar(p,sizeof(p),0);
132   while m>0 do
133   begin
134     dec(m);
135     read(s);
136     for i:=1 to s do
137       read(a[i]);
138 
139     sort(1,s);
140     w:=1;
141     for i:=2 to s do
142       if lca(a[w],a[i])<>a[w] then  //这里一点点优化,如果存在祖先关系的关键点,肯定割祖先以上的边
143       begin
144         inc(w);
145         a[w]:=a[i];
146       end;
147     len:=0;
148     st[1]:=1;
149     t:=1;
150     for i:=1 to w do
151     begin
152       x:=a[i];
153       z:=lca(x,st[t]); 
154       while d[z]<d[st[t]] do  //这里画个图比较好理解,类似一个新路径和原来维护的路径的一个分叉
155       begin
156         if d[z]>=d[st[t-1]] then
157         begin
158           add(z,st[t],0);
159           dec(t);
160           if st[t]<>z then
161           begin
162             inc(t);
163             st[t]:=z;
164           end;
165           break;
166         end;
167         add(st[t-1],st[t],0);  //把原来分叉的那段建出来
168         dec(t);
169       end;
170       if st[t]<>x then
171       begin
172         inc(t);
173         st[t]:=x;
174       end;
175     end;
176     while t>1 do   //把维护的路径建出来
177     begin
178       add(st[t-1],st[t],0);
179       dec(t);
180     end;
181     dp(1);
182     writeln(f[1]);
183   end;
184 end.
View Code
原文地址:https://www.cnblogs.com/phile/p/4472966.html