BZOJ 2815 浅谈有向图必经点问题总结+拓扑序+倍增LCA灭绝树求法

BZOJ 2815 浅谈有向图必经点问题总结+拓扑序+倍增LCA灭绝树求法_第1张图片
世界真的很大
昨天算是感觉到了真的有人这么无聊
就是有这种人,也管不得了,还是收起心情才是
必经点问题在考试中也算是出现过好几次了,之前都用了其他的蜜汁方法水过去,昨天终究还是用了什么灭绝树
感觉还是要总结一下必经点这么一个东西了
当然听说支配树是可以完全搞定所有问题的,不过常数较大罢了,这个就不讨论了

看题先:

description:

http://www.lydsy.com/JudgeOnline/upload/zjoi2012.pdf
题面是一个在线的PDF

input:

如题

output:

如题

先来谈谈这道题,引用一下出题人的题解:
谈谈自己的想法。
这个必经点,其实感觉是有一个类拓扑序的关系的
比如u能到v,那么1到v的必经点一定是1到u的必经点。
前提是这张图是一个DAG
1到v的必经点一定是所有能到他的点的必经点的交集
这个应该比较好理解
于是我就可以通过在跑拓扑序的时候来构建这棵树
1到树上的一个点u的路径上的所有点都是1到u的必经点
那么既然我们要找连到v的所有点的必经点的交集,所以在树上找lca就行了,把v连到他们的lca的下面


现在来谈谈必经点的总结了
原来考过一道求必经点的题,是求的两点之间的所有必进点,图是一张DAG
除了灭绝树以外,当时采用的方法是建一张反图,看哪些点是1能到且能到n的,把这张图扣出来,建成无向图求割点

后来又来一道题,有向图变成了无向图,还是两点之间的必经点
由于是有向图无法建反图,无法扣图,但是可以直接跑tarjan了
当时使用的方法是直接求割点,顺便记一个DFS序(伪),找到割点之后判断n在不在割点的v的子树里
这里的v指的是判断这个点是割点的哪一个点v

然后就是昨天这道题了
DAG上单源多路劲必经点
除了灭绝树以外暂且没有其他方法,支配树除外


完整代码:

#include
#include
#include
#include
using namespace std;

struct edge
{
    int v,last;
}ed[200010];

vector <int> frm[100010];

int n,S=0,num=0;
int head[100010],anc[100010][22],dep[100010],siz[100010],in[100010];

void add(int u,int v)
{
    num++,in[v]++;
    ed[num].v=v;
    ed[num].last=head[u];
    head[u]=num;
}

int lca(int u,int v)
{
    if(dep[u]for(int i=20;i>=0;i--)
        if(dep[anc[u][i]]>=dep[v])
            u=anc[u][i];
    if(u==v) return v;
    for(int i=20;i>=0;i--)
        if(anc[u][i]!=anc[v][i])
            u=anc[u][i],v=anc[v][i];
    return anc[u][0];
}

void init(int u)
{
    int f=0;
    for(int i=0;iif(!f) f=frm[u][i]; else f=lca(frm[u][i],f);
    anc[u][0]=f,dep[u]=dep[f]+1;
    for(int i=1;i<=20;i++)
        anc[u][i]=anc[anc[u][i-1]][i-1];
}

void TOP()
{
    queue <int> state;
    state.push(S);
    while(!state.empty())
    {
        int u=state.front();
        state.pop();
        for(int i=head[u];i;i=ed[i].last)
        {
            int v=ed[i].v;
            in[v]--,frm[v].push_back(u);
            if(!in[v]) state.push(v),init(v);
        }
    }
}

void ReBuild()
{
    num=0;
    memset(head,0,sizeof(head));
    for(int i=1;i<=n;i++)
        add(anc[i][0],i);
}

void dfs(int u)
{
    siz[u]=1;
    for(int i=head[u];i;i=ed[i].last)
    {
        int v=ed[i].v;
        dfs(v);
        siz[u]+=siz[v];
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int u;
        while(scanf("%d",&u) && u) 
            add(u,i);
    }
    for(int i=1;i<=n;i++)
        if(!in[i]) add(S,i);
    TOP();
    ReBuild();
    dfs(S);
    for(int i=1;i<=n;i++)
        printf("%d\n",siz[i]-1);
    return 0;
} 

嗯,就是这样

你可能感兴趣的:(BZOJ,奇妙题,灭绝树)