【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;
}
时间: 2024-10-25 16:06:50

【BZOJ3572】【Hnoi2014】世界树 虚树的相关文章

【BZOJ-3572】世界树 虚树 + 树形DP

3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 611[Submit][Status][Discuss] Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石.世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种

BZOJ 3572: [Hnoi2014]世界树 [虚树 DP 倍增]

传送门 题意: 一棵树,多次询问,给出$m$个点,求有几个点到给定点最近 写了一晚上... 当然要建虚树了,但是怎么$DP$啊 大爷题解传送门 我们先求出到虚树上某个点最近的关键点 然后枚举所有的边$(f,x)$,讨论一下边上的点的子树应该靠谁更近 倍增求出分界点 注意有些没出现在虚树上的子树 注意讨论的时候只讨论链上的不包括端点,否则$f$的子树会被贡献多次 学到的一些$trick:$ 1.$pair$的妙用 2.不需要建出虚树只要求虚树的$dfs$序(拓扑序)和$fa$就可以$DP$了 注意

[HNOI2014][BZOJ3572] 世界树|虚树|树上倍增LCA|树型dp|dfs序

3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 555  Solved: 319[Submit][Status][Discuss] Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石.    世界树的形态可以用一个数学模型来描述:世界树中有n个种

BZOJ3572 [Hnoi2014]世界树 【虚树 + 树形dp】

题目 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石. 世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种族的编号分别从1到n,分别生活在编号为1到n的聚居地上,种族的编号与其聚居地的编号相同.有的聚居地之间有双向的道路相连,道路的长度为1.保证连接的方式会形成一棵树结构,即所有的聚居地之间可以互相到达,并且不会出现环.定义两个聚居地之间的距

bzoj3572[Hnoi2014]世界树

http://www.lydsy.com/JudgeOnline/problem.php?id=3572 首先我们先构建出虚树 然后在虚树上DP,求出虚树上每个点离最近的临时议事处在哪里 对于虚树上相邻的两个点$u$和$v$,他们连线上一定存在一个分界处,一边一定会去离$u$最近的临时议事处:另一边一定会去离$v$最近的临时议事处 然后就做完了 #include<cstdio> #include<cstdlib> #include<iostream> #include&

BZOJ 3572 世界树(虚树)

http://www.lydsy.com/JudgeOnline/problem.php?id=3572 思路:建立虚树,然后可以发现,每条边不是同归属于一端,那就是切开,一半给上面,一半给下面. 1 #include<algorithm> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include<iostream> 6 #define N 300005 7 int t

bzoj千题计划255:bzoj3572: [Hnoi2014]世界树

http://www.lydsy.com/JudgeOnline/problem.php?id=3572 明显需要构造虚树 点属于谁管理分三种情况: 1.属于虚树的点 2.在虚树上的边上的点 3.既不属于虚树的点,又不属于虚树上的边的点 第一种情况: 先做一遍树形dp,得到子树中距离它最近的点 再dfs一遍,看看父节点那一块 是否有比它现在的点更近的点 第二种情况: 一条边u-->v 如果u和v属于同一点x管理,那么这条边所代表的所有点也都属于x管理 否则的话,二分一个点tmp,tmp以上的点归

luogu3233 世界树 (虚树)

反正肯定要建虚树,考虑建完之后怎么做 先随便dp一下算出来距离某点最近的询问点mi[x](因为有的虚树上的点它不是询问点嘛) 那我们对于某条链x到fa[x]上的非虚树上的点(包括他们的非虚树上的孩子),要么把它分给mi[x],要么分给mi[fa[x]] 我找到这个中间点以后,在原树上倍增跳过去,算他的size 这个分法是以$\frac{len[mi[x]]+len[x]+len[mi[fa[x]]]}{2}$再加加减减一些细节决定的(len[x]表示x到fa[x]的链的长度) 除此之外,每个在虚

bzoj 3572 [Hnoi2014]世界树(虚树+DP)

3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 645  Solved: 362[Submit][Status][Discuss] Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石.     世界树的形态可以用一个数学模型来描述:世界树中有n个