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以上的点归管理u的点管理,tmp及tmp以下的点归管理v的点管理

第三种情况:

归这个种树的根 的父节点 管理

第三种情况可以合并到第一种情况中,即用siz[x]表示虚树中一个点x在原树代表多少个点

开始siz[x]=原树中x的子树大小

虚树中每加一条边x-->y,若y属于x的子节点中k的子树,就在siz[x]中减去 原树中k的子树大小

#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>

#define N 300001

typedef long long LL;

#define min(x,y) ((x)<(y) ? (x) : (y))

int n,lim;
int num,id[N];
int fa[N][19],SIZ[N],prefix[N],dep[N];

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-‘0‘;c=getchar(); }
}

namespace Original
{
    int front[N],nxt[N<<1],to[N<<1];
    int tot;

    void add(int u,int v)
    {
        to[++tot]=v; nxt[tot]=front[u]; front[u]=tot;
        to[++tot]=u; nxt[tot]=front[v]; front[v]=tot;
    }    

    void dfs(int x)
    {
        id[x]=++num;
        SIZ[x]=1;
        int t;
        for(int i=front[x];i;i=nxt[i])
        {
            t=to[i];
            if(t!=fa[x][0])
            {
                fa[t][0]=x;
                dep[t]=dep[x]+1;
                dfs(t);
                SIZ[x]+=SIZ[t];
            }
        }
    }        

    void multiplication()
    {
        lim=log(n)/log(2);
        for(int i=1;i<=lim;++i)
            for(int j=1;j<=n;++j)
                fa[j][i]=fa[fa[j][i-1]][i-1];
    }

    void Prefix_dfs(int x)
    {
        int t;
        for(int i=front[x];i;i=nxt[i])
        {
            t=to[i];
            if(t!=fa[x][0])
            {
                prefix[t]=prefix[x]+SIZ[x]-SIZ[t];
                Prefix_dfs(t);
            }
        }
    }

    void main()
    {
        int u,v;
        read(n);
        for(int i=1;i<n;++i)
        {
            read(u); read(v);
            add(u,v);
        }
        dfs(1);
        multiplication();
        Prefix_dfs(1);
        return;
    }
}

namespace Imaginary
{
    int cnt,use[N];

    int st[N],top;

    int tot;
    int front[N],to[N],nxt[N],from[N],val[N];

    int bin[N],bin_cnt;

    int siz[N];

    int mi[N],bl[N];
    int dy[N],ans[N];

    bool cmp(int p,int q)
    {
        return id[p]<id[q];
    }

    int find_ancestor(int x,int y)
    {
        for(int i=lim;i>=0;--i)
            if(y>=(1<<i))
            {
                x=fa[x][i];
                y-=(1<<i);
            }
        return x;
    }

    void add(int u,int v,int w)
    {
        to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; from[tot]=u; val[tot]=w;
        int s=find_ancestor(v,dep[v]-dep[u]-1);
        siz[u]-=SIZ[s];
    }

    int get_lca(int x,int y)
    {
        if(id[x]<id[y]) std::swap(x,y);
        for(int i=lim;i>=0;--i)
            if(id[fa[x][i]]>id[y]) x=fa[x][i];
        return fa[x][0];
    }

    int get_dis(int u,int v)
    {
        int lca=get_lca(u,v);
        return dep[u]+dep[v]-dep[lca]*2;
    }

    void build()
    {
        std::sort(use+1,use+cnt+1,cmp);
        tot=0;
        st[top=1]=1;
        bin[bin_cnt=1]=1;
        siz[1]=SIZ[1];
        int i=1;
        if(use[1]==1) i=2;
        int x,lca;
        for(;i<=cnt;++i)
        {
            x=use[i];
            lca=get_lca(x,st[top]);
            while(id[lca]<id[st[top]])
            {
                if(id[lca]>=id[st[top-1]])
                {
                    add(lca,st[top],dep[st[top]]-dep[lca]);
                    if(lca!=st[--top])
                    {
                        st[++top]=lca;
                        siz[lca]+=SIZ[lca];
                        bin[++bin_cnt]=lca;
                    }
                    break;
                }
                add(st[top-1],st[top],dep[st[top]]-dep[st[top-1]]);
                top--;
            }
            st[++top]=x;
            siz[x]+=SIZ[x];
            bin[++bin_cnt]=x;
        }
        while(top>1)
        {
            add(st[top-1],st[top],dep[st[top]]-dep[st[top-1]]);
            top--;
        }
    }

    int dfs1(int x)
    {
        int p,d;
        mi[x]=0;
        if(dy[x])
        {
            for(int i=front[x];i;i=nxt[i]) dfs1(to[i]);
            bl[x]=x;
            return x;
        }
        for(int i=front[x];i;i=nxt[i])
        {
            p=dfs1(to[i]);
            d=dep[p]-dep[x];
            if(!mi[x] || d<mi[x])
            {
                mi[x]=d;
                bl[x]=p;
            }
            else if(d==mi[x] && p<bl[x]) bl[x]=p;
        }
        return bl[x];
    }

    int dfs2(int x)
    {
        int t;
        for(int i=front[x];i;i=nxt[i])
        {
            t=to[i];
            if(!dy[t])
                if(bl[x]!=bl[t])
                {
                    if(mi[x]+val[i]<mi[t])
                    {
                        mi[t]=mi[x]+val[i];
                        bl[t]=bl[x];
                    }
                    else if(mi[x]+val[i]==mi[t] && bl[x]<bl[t]) bl[t]=bl[x];
                }
            dfs2(t);
        }
        ans[dy[bl[x]]]+=siz[x];
    }

    void belong()
    {
        dfs1(1);
        dfs2(1);
    }

    void get_ans()
    {
        int f,s;
        int l,r,mid,tmp,tmp_son;
        int u,v;
        bool equal;
        int up,down;
        for(int i=1;i<=tot;++i)
        {
            u=from[i];
            v=to[i];
            r=dep[v]-dep[u]-1;
            if(!r) continue;
            s=find_ancestor(v,r);
            if(bl[u]==bl[v]) ans[dy[bl[u]]]+=prefix[v]-prefix[s];
            else
            {
                tmp=v;
                l=1;
                equal=false;
                while(l<=r)
                {
                    mid=l+r>>1;
                    f=find_ancestor(v,mid);
                    down=get_dis(f,bl[v]);
                    up=get_dis(f,bl[u]);
                    if(down<up) tmp=f,l=mid+1;
                    else if(down==up)
                    {
                        tmp=f;
                        equal=true;
                        tmp_son=find_ancestor(v,mid-1);
                        break;
                    }
                    else r=mid-1;
                }
                if(!equal)
                {
                    ans[dy[bl[v]]]+=prefix[v]-prefix[tmp];
                    ans[dy[bl[u]]]+=prefix[tmp]-prefix[s];
                }
                else
                {
                    ans[dy[bl[v]]]+=prefix[v]-prefix[tmp_son];
                    ans[dy[bl[u]]]+=prefix[tmp]-prefix[s];
                    ans[dy[min(bl[v],bl[u])]]+=prefix[tmp_son]-prefix[tmp];
                }
            }
        }
        for(int i=1;i<=cnt;++i) printf("%d ",ans[i]);
        printf("\n");
    }

    void clear()
    {
        for(int i=1;i<=bin_cnt;++i)
        {
            front[bin[i]]=0;
            ans[i]=0;
            dy[bin[i]]=0;
            siz[bin[i]]=0;
        }
        tot=0;
    }

    void main()
    {
        int m;
        read(m);
        while(m--)
        {
            read(cnt);
            for(int i=1;i<=cnt;++i)
            {
                read(use[i]);
                dy[use[i]]=i;
            }
            build();
            belong();
            get_ans();
            clear();
        }
        return;
    }
}

int main()
{
    Original::main();
    Imaginary::main();
    return 0;
}

原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8497215.html

时间: 2024-10-06 04:23:04

bzoj千题计划255:bzoj3572: [Hnoi2014]世界树的相关文章

bzoj千题计划185:bzoj1260: [CQOI2007]涂色paint

http://www.lydsy.com/JudgeOnline/problem.php?id=1260 区间DP模型 dp[l][r] 表示涂完区间[l,r]所需的最少次数 从小到大们枚举区间[l,r] 如果col[l]==col[r] dp[l][r]=min(dp[l+1][r],dp[l][r-1],dp[l+1][r-1]+1) 否则 dp[l][r]=min(dp[l][k]+dp[k+1][r]) 我还是辣鸡啊~~~~(>_<)~~~~,这种题都不能秒 #include<c

bzoj千题计划292:bzoj2244: [SDOI2011]拦截导弹

http://www.lydsy.com/JudgeOnline/problem.php?id=2244 每枚导弹成功拦截的概率 = 包含它的最长上升子序列个数/最长上升子序列总个数 pre_len [i] 表示以i结尾的最长不下降子序列的长度 pre_sum[i] 表示对应长度下的方案数 suf_len[i] 表示以i开头的最长不下降子序列长度 suf_sum[i] 表示对应长度下的方案数 若已有了这4个数组 设最长上升子序列长度=mx 那么 如果pre_len[i]+suf_len[i] -

bzoj千题计划304:bzoj3676: [Apio2014]回文串

https://www.lydsy.com/JudgeOnline/problem.php?id=3676 回文自动机模板题 4年前的APIO如今竟沦为模板,,,╮(╯▽╰)╭,唉 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 300001 char ss[N]; int s[N]; int tot=1,last; int fail[N],len

bzoj千题计划106:bzoj1014 [JSOI2008]火星人prefix

http://www.lydsy.com/JudgeOnline/problem.php?id=1014 两个后缀的最长公共前缀:二分+hash 带修改带插入:splay维护 #include<cstdio> #include<cstring> #include<iostream> #define L 100001 typedef unsigned long long ULL; using namespace std; char s[L+4]; int tot,root

bzoj千题计划108:bzoj1018: [SHOI2008]堵塞的交通traffic

http://www.lydsy.com/JudgeOnline/problem.php?id=1018 关键点在于只有两行 所以一个2*m矩形连通情况只有6种 编号即对应代码中的a数组 线段树维护 用b数组表示 节点第0/1行的最右一列是否连接了右边 来 辅助 节点的合并 查询 对两个点位于矩形的位置分4种情况讨论 两点是否联通,要考虑四种情况 (以两个位置是矩形左上角和右上角为例) 1.直接联通,线段树的节点包含了这种情况,直接判断 2. 3. 4. 后三种情况需要再查询[1,l]和[r,n

bzoj千题计划109:bzoj1019: [SHOI2008]汉诺塔

http://www.lydsy.com/JudgeOnline/problem.php?id=1019 题目中问步骤数,没说最少 可以大胆猜测移动方案唯一 (真的是唯一但不会证) 设f[i][j] 表示 从i号柱子 上把j个盘子移到 g[i][j] 柱子上的步数 初始化:f[0][1]=1,g[0][1] 根据优先级决定 设三根柱子分别为0,1,2 对于每一个f[x][i], 把前i-1个移走,把第i个移走,把前i-1个移回 令y=g[x][i-1],则k=0+1+2-x-y 我们希望 把i-

bzoj千题计划111:bzoj1021: [SHOI2008]Debt 循环的债务

http://www.lydsy.com/JudgeOnline/problem.php?id=1021 如果A收到了B的1张10元,那么A绝对不会把这张10元再给C 因为这样不如B直接给C优 由此可以推出 若A欠B20元,B欠C 30元, 那么A还C20元,B还C10元最优 所以一共只有 A->BC   B->AC  C->AB AB->C  BC->A  AC->B 这6种转移情况 根据输入,我们可以知道三人最终手中有多少钱ea.eb.ec,一共有多少钱sum 设f

bzoj千题计划112:bzoj1022: [SHOI2008]小约翰的游戏John

http://www.lydsy.com/JudgeOnline/problem.php?id=1022 http://www.cnblogs.com/TheRoadToTheGold/p/6744825.html #include<cstdio> #include<iostream> using namespace std; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar();

bzoj千题计划113:bzoj1023: [SHOI2008]cactus仙人掌图

http://www.lydsy.com/JudgeOnline/problem.php?id=1023 dp[x] 表示以x为端点的最长链 子节点与x不在同一个环上,那就是两条最长半链长度 子节点与x在同一个环上,环形DP,单调队列优化 对于每一个环,深度最小的那个点 有可能会更新 上层节点, 所以 每一个环DP完之后,更新 dp[深度最小的点] #include<cstdio> #include<iostream> #include<algorithm> using