支配树
DAG 支配树
对DAG进行拓扑排序。\(u\) 的支配树上父亲节点即 \(u\) 的所有前驱在支配树上的 lca。
CF757F Team Rocket Rises Again
建出最短路图的支配树,题目问的即树上最大真子树。
普通有向图的支配树 Lengauer Tarjan
考虑一张普通有向图的支配树。我们进行一些定义
- \(s_u\) 半支配点。\(s_u\) 为 \(dfn\) 最小的点 \(v\) 满足 \(v\to u\) 有一条路径满足除了 \(v,u\) 剩余点的 \(dfn\) 都 \(> dfn_u\)。
- \(d_u\) 支配点。没啥好定义的。
对于边 \(v\to u\),考虑两种情况
- \(dfn_v<dfn_u\),\(v\) 可能是 \(u\) 的半支配点(路径:\(v\to u\))。
- \(dfn_v>dfn_u\),\(v\) 的祖先 \(w\) 的半支配点 \(s_w\) 可能是 \(u\) 的半支配点(路径:\(s_w\to w\to v\to u\))。
我们考虑由 dfn 从大往小做。对于一个点 \(u\),遍历 \(v\to u\),考虑两种情况。第一种情况好做,第二种情况考虑维护一个带权并查集, 维护 \(v\) 的已经访问过的祖先半支配点 \(dfn\) 最小的 \(w\),然后用 \(s_w\) 去更新 \(s_u\)。
对于如何求支配点。我们把 dfs 树和半支配树的边集组成一个 DAG。这个 DAG 的支配树即原图支配树。感性理解(雾
#include<bits/stdc++.h>
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=3e5+9;
inline long long read() {
register long long x=0, f=1; register char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
return x*f;
}
int n,m,tick,dfn[N],did[N],anc[N],s[N],deg[N],dep[N],lc[N],f[N][29],sz[N];
vector<int>e[N],re[N],de[N],te[N];
int id[N],ms[N];
int find(int u) {
if(u==id[u]) return u;
int of=id[u]; id[u]=find(id[u]);
if(dfn[s[ms[of]]]<dfn[s[ms[u]]]) ms[u]=ms[of];
return id[u];
}
void dfs1(int u,int fa) {
dfn[u]=++tick, did[tick]=u, anc[u]=fa;
for(auto v:e[u]) if(!dfn[v]) dfs1(v,u);
}
void get_semi() {
per(dt,n,2) {
int u=did[dt],res=n; if(!u) continue;
for(auto v:re[u]) if(dfn[v]) {
if(dfn[v]<dfn[u]) res=min(res,dfn[v]);
else find(v),res=min(res,dfn[s[ms[v]]]);
}
s[u]=did[res], id[u]=anc[u];
}
rep(i,2,n) de[anc[i]].push_back(i), de[s[i]].push_back(i), deg[i]=2;
}
void build(int u) {
dep[u]=dep[lc[u]]+1, f[u][0]=lc[u];
te[lc[u]].push_back(u);
rep(i,1,19) f[u][i]=f[f[u][i-1]][i-1];
}
int lca(int u,int v) {
if(dep[u]<dep[v]) swap(u,v);
per(h,19,0) if(dep[f[u][h]]>=dep[v]) u=f[u][h];
if(u==v) return u;
per(h,19,0) if(f[u][h]!=f[v][h]) u=f[u][h],v=f[v][h];
return f[u][0];
}
void get_dom() {
queue<int>q; q.push(1);
dep[1]=1;
while(!q.empty()) {
int u=q.front(); q.pop();
build(u);
for(auto v:de[u]) {
deg[v]--;
lc[v]=(lc[v]?lca(lc[v],u):u);
if(!deg[v]) q.push(v);
}
}
}
void dfs2(int u) {
for(auto v:te[u]) dfs2(v), sz[u]+=sz[v];
sz[u]++;
}
int main() {
n=read(), m=read();
rep(i,1,m) {
int u=read(), v=read();
e[u].push_back(v), re[v].push_back(u);
}
rep(i,1,n) id[i]=ms[i]=s[i]=i;
dfs1(1,0);
get_semi();
get_dom();
dfs2(1);
rep(i,1,n) printf("%d ",sz[i]); puts("");
return 0;
}