bzoj 3572 分类: bzoj 2015-03-29 22:23 45人阅读 评论(0) 收藏
虚树:包含了给定点,并收缩了不分叉边的连通子图。
http://www.kuangbin.net/archives/bzoj3572虚树的构建:
将所有询问点按照dfs序排一遍序,然后,依次加入栈中,若枚举的节点h[i]与栈顶元素的s[top] 的LCA fa的深度比栈顶元素深度小,s[top-1]向s[top]连接一条边,栈顶弹出,fa与h[i]都加入栈中。就这样,一颗虚树建立出来了。
http://www.cnblogs.com/mmlz/p/4278949.html虚树的dp:
分几种情况来讨论:
1.首先,虚树中没有父亲的点p一定是最高的点,有大小为n-size[p]的点与他共用最近点。
2.每个虚树上的点和他没在虚树上的子树肯定会共用一个最近点这个我们只要用其总的size减去在虚树中的size最后统计答案即可。
3.对于虚树某一对父子i,fa,若他们共用一对最近点,则他们在树上的链上点也一定共用这个最近点。
4.若并不共用最近点,肯定有某一段与fa共用最近点,另一段与i共用最近点。因此我们需要找到这个分界点p。由于p到i最近点的距离与p到fa最近点的距离尽可能的接近,所以有dis(i,p)=(near[fa]-near[i]+dis(i,fa))/2,这样得出来的点是最接近的,并且考虑了不能整除的情况,但是仍然需要特判一些情况——p到i与p到fa的距离一样近,我们就需要比较他们点的大小,适当的调整p的位置。
http://www.cnblogs.com/mmlz/p/4279056.html
代码是抄的,别问我为什么,就是自己写不出吧。。。
#include<map>
#include<string>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
//bzoj3572
const int MAXN = 300005 ,logN = 20 ,INF = 1<<30;
int n , head[MAXN] = {0}, ml = 0;
struct edgetype{int v,next;}edge[MAXN<<1] = {0};
int fa[MAXN][logN] = {0}, dep[MAXN] = {0}, size[MAXN] = {0};
int DFN[MAXN] = {0}, dl = 0;
int q ;
void build(int a)
{
DFN[a]= ++dl;
dep[a] = dep[fa[a][0]]+1;
size[a] = 1;
for(int i = head[a]; i ; i = edge[i].next)
{
int p = edge[i].v;
if(p == fa[a][0])continue;
fa[p][0] = a; build(p);
size[a] += size[p];
}
return;
}
void prelca()
{
for(int j = 1 ;j < logN; j++)
for(int i = 1; i <= n ; i++)
fa[i][j] = fa[fa[i][j-1]][j-1];
}
void newedge(int u,int v)
{
++ml;edge[ml].v = v;
edge[ml].next = head[u];
head[u] = ml;
}
int h0[MAXN],h[MAXN],ans[MAXN];
int vfa[MAXN],t[MAXN],val[MAXN];
int stack[MAXN],w[MAXN];
std::pair<int,int>g[MAXN];
#define Wei first
#define Num second
#define Mp(x,y) std::make_pair(x,y)
int getLCA(int u,int v)
{
if(dep[u]<dep[v])std::swap(u,v);//dep[u]>=dep[v]
for(int i = logN - 1; i >= 0;i--)
if(dep[fa[u][i]] >= dep[v]) u = fa[u][i];
if(u == v)return u;
for(int i = logN - 1; i >= 0; i--)
if(fa[u][i]!=fa[v][i])
u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
int upto(int u ,int des)
{
for(int i = logN - 1; i >= 0;i--)
if(dep[fa[u][i]] >= des) u = fa[u][i];
return u;
}
bool cmp(const int a,const int b){return (DFN[a]<DFN[b]);}
void solve()
{
int m ,tot = 0;
scanf("%d",&m);
for(int i = 1; i <= m; i++)
{
scanf("%d",&h[i]);
h0[i] = h[i];
t[++tot] = h[i];
g[h[i]] = Mp(0,h[i]);
ans[h[i]] = 0;
}
std::sort(h + 1,h + m + 1,cmp);
int top = 0;
for(int i = 1; i <= m; i++)
if(!top) vfa[stack[++top] = h[i]] = 0;
else
{
int p = h[i], lca = getLCA(p,stack[top]);
while(dep[stack[top]] > dep[lca])
{
if(dep[stack[top-1]] <= dep[lca])
vfa[stack[top]] = lca;
top--;
}
if(stack[top] != lca)
{
t[++tot] = lca;
g[lca] = Mp(INF , 0);
vfa[lca] = stack[top];
stack[++top] = lca;
}
vfa[p] = lca;
stack[++top] = p;
}
//build the virtual tree
std::sort(t + 1 , t + tot + 1 ,cmp);
for(int i = 1 ; i <= tot ; i++)
{
int p = t[i]; val[p] = size[p];
if(i!=1)w[p] = dep[p] - dep[vfa[p]];
}
for(int i = tot; i > 1 ; i--)
{
int p = t[i], fi = vfa[p];
g[fi] = std::min(Mp(g[p].Wei+w[p],g[p].Num),g[fi]);
}
for(int i = 2; i <= tot; i++)
{
int p = t[i], fi = vfa[p];
g[p] = std::min(Mp(g[fi].Wei+w[p],g[fi].Num),g[p]);
}//BFS
for(int i = 1 ;i <= tot; i++)
{
int p = t[i], fi = vfa[p];
if(i == 1) ans[g[p].Num] += size[1] - size[p];
else
{
int x = upto(p,dep[fi]+1), sum = size[x] - size[p];
val[fi] -= size[x];
if(g[fi].Num == g[p].Num)ans[g[p].Num] += sum;
else
{
int tmp = g[fi].Wei + g[p].Wei + w[p];
int mid = dep[p] - ((tmp>>1) - g[p].Wei);
if((tmp&1)==0 && g[p].Num > g[fi].Num) mid++;
int y = size[upto(p,mid)] - size[p];
ans[g[p].Num] += y;
ans[g[fi].Num] += sum - y;
}
}
}//Dp
for(int i = 1 ; i <= tot; i++)
ans[g[t[i]].Num] += val[t[i]];
for(int i = 1 ; i <= m ; i++)
printf("%d ",ans[h0[i]]);
printf("\n");
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("worldtree.in","r",stdin);
freopen("worldtree.out","w",stdout);
#endif
scanf("%d",&n);
for(int i = 1; i < n ; i++)
{
int a,b;scanf("%d%d",&a,&b);
newedge(a,b);newedge(b,a);
}
fa[1][0] = 0;
build(1);prelca();
scanf("%d",&q);
while(q--)solve();
#ifndef ONLINE_JUDGE
fclose(stdin);
fclose(stdout);
#endif
}