支配树(洛谷-P5180)

题目描述

给定一张有向图,求从1号点出发,每个点能支配的点的个数(包括自己)

输入输出格式

输入格式:

第一行两个正整数n,mn,m,表示点数和边数 接下来mm行,每行输入两个整数u,vu,v,表示有一条uu到vv的有向边

输出格式:

一行输出nn个整数,表示每个点能支配的点的个数

输入输出样例

输入样例#1:

10 15
1 2
2 3
3 4
3 5
3 6
4 7
7 8
7 9
7 10
5 6
6 8
7 8
4 1
3 6
5 3

输出样例#1:

10 9 8 4 1 1 3 1 1 1 

思路:求一般有向图的支配点,支配树的 Lengauer Tarjan 算法模板题

源代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<unordered_map>
#include<bitset>
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<LL,LL>
LL quickPow(LL a,LL b){ LL res=1; while(b){if(b&1)res*=a; a*=a; b>>=1;} return res; }
LL quickModPow(LL a,LL b,LL mod){ LL res=1; a=a%mod; while(b){if(b&1)res=(a*res)%mod; a=(a*a)%mod; b>>=1;} return res; }
LL getInv(LL a,LL mod){ return quickModPow(a,mod-2,mod); }
LL GCD(LL x,LL y){ return !y?x:GCD(y,x%y); }
LL LCM(LL x,LL y){ return x/GCD(x,y)*y; }
const double EPS = 1E-10;
const int MOD = 998244353;
const int N = 400000+5;
const int dx[] = {-1,1,0,0,1,-1,1,1};
const int dy[] = {0,0,-1,1,-1,1,-1,1};
using namespace std;

struct MAP {
    struct Edge {
        int to, next;
    } edge[N << 1];
    int tot, head[N];
    void addEdge(int x, int y) {
        edge[++tot].to = y;
        edge[tot].next = head[x];
        head[x] = tot;
    }
};
MAP G, GF;             //原图、反图
MAP dfsTree, dfsTreeF; // dfs树、dfs树的反图
MAP dominate;          //支配树

MAP xx;
int n, m;
int father[N];          // dfs树上的父节点
int dfn[N], id[N], tim; // dfs序、标号、时间戳

void dfs(int x) {
    id[++tim] = x;
    dfn[x] = tim;

    for (int i = G.head[x]; i; i = G.edge[i].next) {
        int to = G.edge[i].to;
        if (!dfn[to]) {
            dfs(to);
            father[to] = x;
            dfsTree.addEdge(x, to);
        }
    }
}

int sdom[N]; //半支配点
int mn[N]; // mn[i]表示i点的dfs树上的sdom最小的祖先,因此有sdom[mn[i]]=sdom[i]
int anc[N];       // anc[i]代表i的祖先
int find(int x) { //路径压缩的带权并查集
    if (x != anc[x]) {
        int t = anc[x];
        anc[x] = find(anc[x]);
        if (dfn[sdom[mn[x]]] > dfn[sdom[mn[t]]])
            mn[x] = mn[t];
    }
    return anc[x];
}
void LengauerTarjan() { //寻找半支配点
    for (int i = 1; i <= n; i++) {
        anc[i] = i;
        sdom[i] = i;
        mn[i] = i;
    }
    for (int j = n; j >= 2; j--) {
        int x = id[j];
        if (!x)
            continue;

        int pos = j;
        for (int i = GF.head[x]; i; i = GF.edge[i].next) {
            int y = GF.edge[i].to;
            if (!dfn[y])
                continue;
            if (dfn[y] < dfn[x])
                pos = min(pos, dfn[y]);
            else {
                find(y); //寻找树上y的一个满足dfn[z]>dfn[x]的祖先z
                pos = min(pos, dfn[sdom[mn[y]]]);
            }
        }
        sdom[x] = id[pos];
        anc[x] = father[x];
        dfsTree.addEdge(sdom[x], x); //在dfs树上连边
    }
}

int deep[N], dp[N][25];
int getLCA(int x, int y) { //获取LCA
    if (deep[x] < deep[y])
        swap(x, y);
    int del = deep[x] - deep[y];
    for (int i = 0; i <= 20; i++)
        if ((1 << i) & del)
            x = dp[x][i];
    if (x == y)
        return x;
    for (int i = 20; i >= 0; i--) {
        if (dp[x][i] != dp[y][i]) {
            x = dp[x][i];
            y = dp[y][i];
        }
    }
    return dp[x][0];
}
void buildDominate(int x) { //建立支配树
    int to = dfsTreeF.edge[dfsTreeF.head[x]].to;
    for (int i = dfsTreeF.head[x]; i; i = dfsTreeF.edge[i].next) {
        int y = dfsTreeF.edge[i].to;
        to = getLCA(to, y);
    }
    deep[x] = deep[to] + 1;
    dp[x][0] = to;
    dominate.addEdge(to, x);

    for (int i = 1; i <= 20; i++)
        dp[x][i] = dp[dp[x][i - 1]][i - 1];
}
int in[N]; // dfs树的入度
void topSort() {
    for (int i = 1; i <= n; i++) {
        for (int j = dfsTree.head[i]; j; j = dfsTree.edge[j].next) {
            int to = dfsTree.edge[j].to;
            in[to]++;
            dfsTreeF.addEdge(to, i); //对DFS树的反图建边
        }
    }
    for (int i = 1; i <= n; i++) {
        if (!in[i]) {
            dfsTree.addEdge(0, i);  // dfs树建边
            dfsTreeF.addEdge(i, 0); // dfs树的反图建边
        }
    }

    queue<int> Q;
    Q.push(0);
    while (Q.size()) {
        int x = Q.front();
        Q.pop();
        for (int i = dfsTree.head[x]; i; i = dfsTree.edge[i].next) {
            int y = dfsTree.edge[i].to;
            if ((--in[y]) <= 0) {
                Q.push(y);
                buildDominate(y); //建立支配树
            }
        }
    }
}

int idom[N];
void dfsDominate(int x) { //在支配树上搜索idom
    idom[x] = 1;
    for (int i = dominate.head[x]; i; i = dominate.edge[i].next) {
        int y = dominate.edge[i].to;
        dfsDominate(y);
        idom[x] += idom[y];
    }
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        G.addEdge(x, y);
        GF.addEdge(y, x);
    }

    dfs(1);           // dfs,求出dfs序
    LengauerTarjan(); //计算半支配点sdom
    topSort(); //根据dfs树建立dfs树的反图,并对进行拓扑排序从而建立支配树
    dfsDominate(0); //在支配树上寻找答案

    for (int i = 1; i <= n; i++)
        printf("%d ", idom[i]);
    printf("\n");
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值