题目描述
给定一张有向图,求从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;
}