码迷,mamicode.com
首页 > 其他好文 > 详细

【BZOJ3572】【Hnoi2014】世界树 虚树

时间:2015-06-15 22:17:07      阅读:140      评论:0      收藏:0      [点我收藏+]

标签:bzoj3572   hnoi2014   世界树   虚树   

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[辗转山河弋流歌 by 空灰冰魂]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/46506883");
}

题解:

首先构建虚树,然后在虚树上DP。
过程很简单。
先找出每个虚树节点 i 旁边最近的询问节点 neari (因为有一些lca也被加入了虚树所以虚树节点不全是询问节点,呃怕你们不懂,但其实这是废话Qwq。)
然后对于每条链 [l,r],找出 [nearl,nearr] 中点,然后两边随便给一给就好了。。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LOGN 20
#define N 301000
#define ti tree[i]
#define pi point[i]
#define inf 0x3f3f3f3f
using namespace std;
struct Eli
{
    int v,n;
}e[N<<1];
int head[N],cnt;
inline void add(int u,int v)
{
    e[++cnt].v=v;
    e[cnt].n=head[u];
    head[u]=cnt;
}
int f[N][LOGN],dep[N];
int size[N],dfn[N];
int init(int x,int p) // 处理出以上四个数组
{
    int i,u,v;
    dfn[x]=++cnt,size[x]=1;
    f[x][0]=p,dep[x]=dep[p]+1;
    for(i=1;f[x][i-1];i++)
        f[x][i]=f[f[x][i-1]][i-1];
    for(i=head[x];i;i=e[i].n)
    {
        v=e[i].v;
        if(v==p)continue;
        size[x]+=init(v,x);
    }
    return size[x];
} // 会不会爆栈我并不想考虑。并不想。因为很不想所以说两遍。
int getlca(int x,int y) // 得到x、y的lca
{
    if(dep[x]<dep[y])swap(x,y);
    for(int i=LOGN-1;~i;i--)
        if(dep[f[x][i]]>=dep[y])
            x=f[x][i];
    if(x==y)return x;
    for(int i=LOGN-1;~i;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}
int find(int x,int d) // 找到x的深度为d的祖先
{
    for(int i=LOGN-1;~i;i--)
        if(dep[f[x][i]]>=d)
            x=f[x][i];
    return x;
}
int n,m,p,rec[N];
bool cmp_dfn(int a,int b){return dfn[a]<dfn[b];}
int stk[N],top,val[N],ans[N];
int point[N],tree[N],fa[N];
int near[N],dis[N],len[N];
void solve()
{
    int i,x,s;

    cnt=top=0;
    scanf("%d",&p);
    for(i=1;i<=p;i++)
    {
        scanf("%d",&pi);
        tree[++cnt]=pi;
        // rec : point[]被排序了,得存一个下来
        rec[i]=near[pi]=pi;
        dis[pi]=ans[pi]=0;
    }
    sort(point+1,point+p+1,cmp_dfn);
    for(i=1;i<=p;i++) // 构建虚树
    {
        if(!top)fa[stk[++top]=pi]=0;
        else {
            int lca=getlca(pi,stk[top]);
            for(;dep[stk[top]]>dep[lca];top--)
                if(dep[stk[top-1]]<=dep[lca])
                    fa[stk[top]]=lca;
            if(stk[top]!=lca)
            {
                fa[lca]=stk[top];
                stk[++top]=tree[++cnt]=lca;
                near[lca]=0,dis[lca]=inf;
            }
            fa[stk[++top]=pi]=lca;
        }
    }
    sort(tree+1,tree+cnt+1,cmp_dfn);
    for(i=1;i<=cnt;i++)
    {
        val[ti]=size[ti];
        len[ti]=dep[ti]-dep[fa[ti]];
    }

    for(i=cnt;i>=2;i--) // 给每个虚树节点找最近要求点
    {
        if(dis[fa[ti]]>dis[ti]+len[ti])
        {
            dis[fa[ti]]=dis[ti]+len[ti];
            near[fa[ti]]=near[ti];
        }
        else if(dis[fa[ti]]==dis[ti]+len[ti])
            near[fa[ti]]=min(near[fa[ti]],near[ti]);
    }
    for(i=2;i<=cnt;i++) // 给每个虚树节点找最近要求点
    {
        if(dis[ti]>dis[fa[ti]]+len[ti])
        {
            dis[ti]=dis[fa[ti]]+len[ti];
            near[ti]=near[fa[ti]];
        }
        else if(dis[ti]==dis[fa[ti]]+len[ti])
            near[ti]=min(near[ti],near[fa[ti]]);
    }
    for(i=1;i<=cnt;i++) // 每条链找中点进行处理
    {
        if(i==1)ans[near[ti]]+=n-size[ti];
        else {
            x=find(ti,dep[fa[ti]]+1);
            s=size[x]-size[ti];
            val[fa[ti]]-=size[x];
            if(near[fa[ti]]==near[ti])
                ans[near[ti]]+=s;
            else {
                int num=dis[fa[ti]]+dis[ti]+len[ti]+1;
                int mid=dep[ti]-((num+1)/2-dis[ti])+1;
                if((num&1)&&near[ti]>near[fa[ti]])mid++;
                x=size[x]-size[find(ti,mid)];
                ans[near[fa[ti]]]+=x;
                ans[near[ti]]+=s-x;
            }
        }
    }
    for(i=1;i<=cnt;i++)ans[near[ti]]+=val[ti];
    for(i=1;i<=p;i++)printf("%d ",ans[rec[i]]);
    puts("");
}
int main()
{   
    int i,j,k;
    int a,b,c;

    scanf("%d",&n);
    for(i=1;i<n;i++)
    {
        scanf("%d%d",&a,&b);
        add(a,b),add(b,a);
    }cnt=0;
    init(1,0);
    for(scanf("%d",&m);m--;)solve();

    return 0;
}

【BZOJ3572】【Hnoi2014】世界树 虚树

标签:bzoj3572   hnoi2014   世界树   虚树   

原文地址:http://blog.csdn.net/vmurder/article/details/46506883

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!