luogu模版题都是黑的,离谱,瑟瑟发抖
一个有向图,钦点一个点 s 为起点。对于两个点 u,v 当删除点 u 使得没有从 s 到 v 的路径存在时,我们称 u 是 v 的支配点。容易发现,对于这种支配关系可以形成一个树形结构,称之为支配树,支配树的根节点是 s 。
树
树的支配树就是他自己啦(
DAG
按照拓扑序进行处理,对于一个点 u ,他在支配树中的父亲是所有直接连向他的点 x 的LCA。
有向图
有环了,难搞。
tarjan是真的神仙!!
首先随便弄一个生成树出来,并且标好dfn序。可以发现横叉边只会从dfn大的地方连向dfn小的地方等性质。
定义半支配点:当存在一条路径 \(u \rightarrow v\) ,并且路径上除 u,v 外的 dfn 都小于 \(dfn[v]\) 的时候,我们称 u 是 v 的半支配点。
我们需要求出对于一个点 u 的 dfn 序最小的半支配点 (semi)。
考虑有 \(w \rightarrow u\) 的情况,二者直接相连。
当 \(dfn[w] < dfn[u]\) 的时候,用 w 来更新 \(semi[u]\) 。
当 \(dfn[w] > dfn[u]\) 的时候,用 w 的 semi 以及他部分祖先的 semi 来更新 \(semi[u]\) 。对于可以更新 u 的 x,有 x 是 w 的本身及祖先且 \(dfn[x] > dfn[u]\) 。
使用带权并查集来进行处理。每次处理完 u 将其连向原dfs树中的father。
有一个性质,我们保留树边和边 \((semi[u] \rightarrow u)\) 后,灭绝树不变。
显然这样处理后是一个 DAG 。可以使用前面提到的 DAG 的做法进行求解即可。
luogu模版code:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
int read()
{
int a = 0,x = 1;char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') x = -1;ch = getchar();}
while(ch >= '0' && ch <= '9') {a = a*10 + ch-'0';ch = getchar();}
return a*x;
}
const int N=1e6+7;
int n,m,head[N],go[N],nxt[N],cnt,anc[N],dfn[N],pos[N],du[N],fa[N],semi[N],mn[N],dp[N],dep[N],jump[N][20],siz[N];
void add(int u,int v)
{
go[++cnt] = v;
nxt[cnt] = head[u];
head[u] = cnt;
}
vector<int>g[N],o[N];
void dfs(int u)
{
dfn[u] = ++cnt,pos[cnt] = u;
for(int e = head[u];e;e = nxt[e]) {
int v = go[e];if(dfn[v]) continue;
anc[v] = u;g[u].push_back(v);du[v] ++;
dfs(v);
}
}
int find(int s)
{
if(fa[s] == s) return s;
int tmp = find(fa[s]);
mn[s] = dfn[semi[mn[s]]]<dfn[semi[mn[fa[s]]]] ? mn[s] : mn[fa[s]];
fa[s] = tmp;return tmp;
}
int LCA(int a,int b)
{
if(!a || !b) return a+b;
if(dep[a] < dep[b]) swap(a,b);
for(int i = 19;i >= 0;i --) if(dep[jump[a][i]] >= dep[b]) a = jump[a][i];
if(a == b) return a;
for(int i = 19;i >= 0;i --) if(jump[a][i] != jump[b][i]) a = jump[a][i],b = jump[b][i];
return jump[a][0];
}
void DFS(int u)
{
siz[u] = 1;
for(int e = head[u];e;e = nxt[e]) {
int v = go[e];DFS(v);
siz[u] += siz[v];
}
}
int main()
{
// freopen("random.in","r",stdin);freopen("out.out","w",stdout);
n = read(),m = read();
for(int i = 1;i <= m;i ++) {
int u = read(),v = read();
add(u,v);o[v].push_back(u);
}
cnt = 0;dfs(1);
for(int i = 1;i <= n;i ++) fa[i] = i,semi[i] = mn[i] = i;
for(int i = n;i >= 2;i --) {
for(int a:o[pos[i]]) {
if(!dfn[a]) continue;
if(dfn[a] < dfn[pos[i]]) semi[pos[i]] = dfn[semi[pos[i]]]>dfn[a]?a:semi[pos[i]];
else find(a),semi[pos[i]] = dfn[semi[pos[i]]]>dfn[semi[mn[a]]]?semi[mn[a]]:semi[pos[i]];
}
g[semi[pos[i]]].push_back(pos[i]);du[pos[i]] ++;fa[pos[i]] = anc[pos[i]];
}
for(int i = 1;i <= n;i ++) head[i] = 0;cnt = 0;
queue<int>q;for(int i = 1;i <= n;i ++) if(!du[i] && dfn[i]) q.push(i);
while(!q.empty()) {
int u = q.front();q.pop();
jump[u][0] = dp[u],dep[u] = dep[dp[u]] + 1;
if(dp[u]) add(dp[u],u);
for(int i = 1;i < 20;i ++) jump[u][i] = jump[jump[u][i-1]][i-1];
for(int v:g[u]) {
du[v] --;dp[v] = LCA(dp[v],u);
if(!du[v]) q.push(v);
}
}
DFS(1);
for(int i = 1;i <= n;i ++) printf("%d ",siz[i]);
return 0;
}