有向图的支配树
支配树
DGA
思路:
求支配树的每个节点的子树大小(不包含自己)
#include<bits/stdc++.h>
#define _ 565535
using namespace std;
int n,rd[_],A[_],dep[_],st[_][21];
vector<int>prey[_],Top[_],tr[_];
int get_lca(int a,int b){
if(dep[a]<dep[b])swap(a,b);
for(int i=20;~i;--i)if(dep[st[a][i]]>=dep[b])a=st[a][i];
if(a==b)return a;
for(int i=20;~i;--i)if(st[a][i]!=st[b][i])a=st[a][i],b=st[b][i];
return st[a][0];
}
void solve(int x){
int lca=prey[x][0];
for(int i=1,l=prey[x].size();i<l;i++)
lca=get_lca(lca,prey[x][i]);
tr[lca].push_back(x);
dep[x]=dep[lca]+1;
st[x][0]=lca;
for(int i=1;st[st[x][i-1]][i-1];i++)
st[x][i]=st[st[x][i-1]][i-1];
}
void topo(){
queue<int>q;
for(int i=1;i<=n;i++)
if(!rd[i])
Top[0].push_back(i),prey[i].push_back(0),rd[i]++;
q.push(0);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0,l=Top[u].size();i<l;i++){
int v=Top[u][i];
rd[v]--;
if(rd[v]==0)q.push(v),solve(v);
}
}
}
void calc(int u){
A[u]=1;
for(int i=0,l=tr[u].size();i<l;i++){
int v=tr[u][i];
calc(v);
A[u]+=A[v];
}
}
int main(){
cin>>n;
for(int i=1,x;i<=n;i++)
while(1){
scanf("%d",&x);
if(x)Top[x].push_back(i),rd[i]++,prey[i].push_back(x);
else break;
}
topo();
calc(0);
for(int i=1;i<=n;i++)printf("%d\n",A[i]-1);
return 0;
}
一般的有向图
思路:
#include<bits/stdc++.h>
#define N 500008
#define M 2000000
#define IL inline
using namespace std;
/*-------------OI使我快乐-------------*/
int n,m,tot,cnt;
int head[M<<1],pre[N<<1],to[N<<2],nex[N<<2],lat[M],cdy[M];
int bel[M],val[M],sdom[M],idom[M],ans[M];
int dfn[M],id[M],fa[M];
IL void add(int *h,int x,int y) {
to[++tot]=y;
nex[tot]=h[x];
h[x]=tot;
}
IL void dfs(int now) {
dfn[now]=++cnt;
id[cnt]=now;
for(int i=head[now]; i; i=nex[i]) {
int v=to[i];
// printf("%d --> %d\n",now,v);
if(dfn[v])
continue;
dfs(v);
fa[v]=now;
}
}
IL int find(int x) {
if(x==bel[x])
return x;
int root=find(bel[x]);
if(dfn[sdom[val[bel[x]]]]<dfn[sdom[val[x]]])
val[x]=val[bel[x]];
return bel[x]=root;
}
IL void Tarjan() {
// for(R int i=1;i<=n;++i) printf("dfn[%d] %d\n",i,dfn[i]);
for(int i=cnt; i>=2; --i) {
int now=id[i];
//我们按照dfs序 从大到小处理 节点
for(int j=pre[now]; j; j=nex[j]) {
//这里用链式前向星 存入度节点
int v=to[j];
if(!dfn[v])
continue;
find(v);
//dfn[y]<dfn[x]的话
//我们还未处理到 sdom[y]=val[y]=y
//此时y的并查集树中只有TA自己 更新是合法的
//dfn[y]>dfn[x]的话
//就按照套路比较
if(dfn[sdom[val[v]]]<dfn[sdom[now]])
sdom[now]=sdom[val[v]];
}
add(lat,sdom[now],now);
bel[now]=fa[now];
//同其在dfs树上的父亲连边
now=fa[now];
//现在父亲到TA这棵子树已经处理完了
//所以此时我们可以对
//以父亲为sdom[x]的x分别求一次sdom 然后清空
//对于每一个点进行更新
for(int j=lat[now]; j; j=nex[j]) {
int v=to[j];
find(v);
if(sdom[val[v]]==now)
idom[v]=now;
//如果sdom[z]==sdom[x] 那么idom[x]=sdom[x]
//此时sdom[x]=now
else
idom[v]=val[v];
//否则就是idom[x]=idom[z]
//但是实际实现我们可以写成idom[x]=z
//然后在接下来处理
}
}
for(int i=2,now; i<=cnt; ++i) {
now=id[i];
if(idom[now]!=sdom[now])
idom[now]=idom[idom[now]];
//这是残余节点的填坑大作战
}
}
IL void dfs_ans(int now) {//支配树
ans[now]=1;
for(int i=cdy[now]; i; i=nex[i]) {
int v=to[i];
dfs_ans(v);
ans[now]+=ans[v];
}
}
int main() {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
scanf("%d%d", &n, &m);
for(int i=1,x,y; i<=m; ++i) {
scanf("%d%d", &x, &y);
add(head,x,y);
add(pre,y,x);
}
for(int i=1; i<=n; ++i)
sdom[i]=bel[i]=val[i]=i;
dfs(1);
Tarjan();
/*
puts("How old are you ? ");
for(int i=1;i<=n;++i)
{
printf("支配点[%d] %d\n",i,idom[i]);
}
*/
tot=0;
for(int i=2; i<=n; ++i)
if(idom[i])
add(cdy,idom[i],i);
dfs_ans(1);
for(int i=1; i<=n; ++i)
printf("%d%c",ans[i],(i==n ? '\n':' '));
// fclose(stdin);
// fclose(stdout);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)