学习记录-DAG上的支配树

基本概念

作用: 在有向图中,当询问从起点到终点的路径必须经过的点,即去掉这个点以及周围的边,就不能从起点到达终点的点(类似于无向图中的割点)。这时,可与通过建立支配树来解决问题。
结构: 支配树理所当然是一个树状结构。图的起点作为根节点,每一个节点到达根节点的路径都是必经点。如果能建立这样的树状结构,那么基本可以通过搜索求得关于必经点的所有信息。
在DAG上的建树方法:
总的来说就是,点x在支配树上的父亲就是所有能走到它的点在支配树上的LCA(这个感性理解)。
这样就可以在O(nlog(n))的时间里求解,而且还是多个点的;
具体讲,就是先进行拓扑排序,然后从前往后依次遍历,没遍历到一个点,需要根据反向边来寻找那哪几个边能到达这个节点,然后求出几个点的最近公共组先,就是在支配树上这个节点的父亲。所以需要用倍增找最近公共组先的方法来动态维护二维 f【】【】 数组(倍增j倍之后i的父节点)和一维 d【】数组(i号节点的深度)。
在普通的有向图中建立支配树:
在普通的有向图中建树还是有一定难度的,看了一遍一知半解,大概思路懂了但是具体实现想的不太清楚,之后持续更新。

一个裸的DAG题目:

链接:https://www.luogu.org/problem/P2597
题目是中文,就不说了。
具体方法就是找每一个点是从起点到几个点的必经点。输出数量。裸的DAG上的支配树建立,建树之后之需要搜索一遍求子树大小即可。
AC代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// #include
// #include
#define pb push_back
#define _fileout freopen("out.txt","w",stdout)
#define _filein freopen("in.txt","r",stdin)
#define ok(i) printf("ok%d\n",i)
using namespace std;
// using namespace __gnu_pbds;
typedef double db;
typedef long long ll;
typedef pair<int,int>PII;
const double PI = acos(-1.0);
const ll MOD=1e9+7;
const ll NEG=1e9+6;
const int MAXN=1e6+10;
const int INF=0x3f3f3f3f;
const ll ll_INF=9223372036854775807;
const double eps=1e-9;
int head[MAXN],nex[MAXN],e[MAXN],o;//正向图
void add(int x,int y)//加正向边
{
    e[++o]=y;
    nex[o]=head[x];head[x]=o;
}
int head2[MAXN],nex2[MAXN],e2[MAXN],o2;//反向图
void add2(int x,int y//加逆向边
{
    e2[++o2]=y;
    nex2[o2]=head2[x];head2[x]=o2;
}
int n;
vector<int>v;
queue<int>q;
int du[MAXN];
// int vis[MAXN];
void getv()//拓扑排序更新拓扑序列v
{
    int x;
    // memset(vis,0,sizeof(vis));
    while(!q.empty())
    {
        x=q.front();
        q.pop();
        // vis[x]=1;
        v.pb(x);
        for(int i=head[x];i;i=nex[i])
        {
            int y=e[i];
            // if(vis[y])continue;
            du[y]--;
            // printf("x=%d y=%d,du=%d\n",x,y,du[y]);
            if(!du[y])q.push(y);
        }
    }
}
int head_zp[MAXN],nex_zp[MAXN],e_zp[MAXN],tot;//支配树
void add_zp(int x,int y)//加支配树的边
{
    e_zp[++tot]=y;
    nex_zp[tot]=head_zp[x];head_zp[x]=tot;
}
int f[MAXN][20],d[MAXN];
int t;
int lca(int x,int y)//求最近公共祖先
{
    if(d[x]>d[y])swap(x,y);
    for(int i=t;i>=0;i--)
    {
        if(d[f[y][i]]>=d[x])y=f[y][i];
    }
    if(x==y)return x;
    for(int i=t;i>=0;i--)
    {
        if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    }
    return f[x][0];
}
int size[MAXN];
void dfs(int x)//搜索求每个节点的子树大小
{
    size[x]=1;
    for(int i=head_zp[x];i;i=nex_zp[i])
    {
        int y=e_zp[i];
        if(size[y]){size[x]+=size[y];continue;}
        dfs(y);
        size[x]+=size[y];
    }
}
int main()
{
    scanf("%d",&n);
    t=(int)(log(n)/log(2))+1;
    for(int i=1;i<=n;i++)
    {
        int x;
        while(~scanf("%d",&x)&&x)
        {
            add(x,i);
            // printf("x=%d i=%d\n",x,i);
            add2(i,x);
            du[i]++;
        }
        // printf("du[%d]=%d\n",i,du[i]);
        if(du[i]==0)
            q.push(i);
    }
    getv();
    int len=v.size();
    // for(int i=0;i
    // {
    //     printf("%d ",v[i]);
    // }
    // printf("\n");
    for(int i=0;i<len;i++)
    {
        int x=v[i];
        int mid=e2[head2[x]];
        for(int j=head2[x];j;j=nex2[j])
        {
            int y=e2[j];
            // ok(0);
            // printf("x=%d y=%d\n",x,y);
            mid=lca(mid,y);
        }
        // printf("%d %d\n",mid,x);
        // printf("lca 2,3=%d\n",lca(2,3));
        // ok(1);
        add_zp(mid,x);
        // ok(2);
        f[x][0]=mid;
        d[x]=d[mid]+1;
        for(int l=1;l<=t;l++)
        {
            f[x][l]=f[f[x][l-1]][l-1];
        }
    }
    // for(int i=1;i<=n;i++)
    // {
    //     for(int j=head_zp[i];j;j=nex_zp[j])
    //     {
    //         int y=e_zp[j];
    //         printf("%d %d\n",i,y);
    //     }
    // }
    for(int i=0;i<len;i++)
    {
        if(!size[v[i]])dfs(v[i]);
        // ok(3);
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d\n",size[i]-1);
    }
    return 0;
}

赏心悦目的AC结果:
学习记录-DAG上的支配树_第1张图片

你可能感兴趣的:(学习记录-图论)