====== 支配树 ====== ===== 算法简介 ===== 给定一个有向图,定义一个超级源点,连向所有入度为 $0$ 的点。 对于点对 $(u,v)$,如果删除 $u$ 将导致超级源点不可以到达 $v$,则称 $u$ 支配 $v$。 易知支配关系构成树,其中每个子树的根节点支配子树的所有结点。称这样的树为支配树。 ===== 算法模板 ===== ==== 有向无环图 ==== [[https://www.luogu.com.cn/problem/P2597|洛谷p2597]] 对每个结点 $v$,设原图中有 $u_1,u_2,u_3\cdots u_k\to v$,易知 $v$ 在支配树上的父结点为 $u_1,u_2\cdots u_k$ 在支配树上的 $\text{LCA}$。 考虑对原图的点进行拓扑,边拓扑边建立支配树,动态维护每个点 $u$ 的当前父结点 $p(u)$。 每次拓扑到 $u$ 时直接在支配树上连边 $p(u)\to u$,然后动态更新原图中 $u\to v$ 的每个 $p(v)\to \text{LCA}(p(v),u)$。 注意 $p(u)$ 初值为 $0$ 且 $\text{LCA}(p(u),0)=p(u)$。至于 $\text{LCA}$ 可以考虑用倍增维护。时间复杂度 $O(m\log n)$。 const int MAXN=1e5+5,MAXM=1e6+5,MAXV=18; struct Edge{ int to,next; }edge[MAXM+MAXN]; int head1[MAXN],head2[MAXN],edge_cnt; int deg[MAXN],f[MAXN],dep[MAXN],anc[MAXN][MAXV],lg2[MAXN]; void Insert1(int u,int v){ edge[++edge_cnt]=Edge{v,head1[u]}; head1[u]=edge_cnt; deg[v]++; } void Insert2(int u,int v){ edge[++edge_cnt]=Edge{v,head2[u]}; head2[u]=edge_cnt; } int LCA(int u,int v){ if(dep[u]dep[v])u=anc[u][lg2[dep[u]-dep[v]]]; if(u==v) return u; for(int i=MAXV-1;i>=0;i--){ if(anc[u][i]!=anc[v][i]) u=anc[u][i],v=anc[v][i]; } return anc[u][0]; } int build(int n){ lg2[1]=0; _for(i,2,MAXN)lg2[i]=lg2[i>>1]+1; int rt=n+1; queue q; _rep(i,1,n){ if(deg[i]==0){ f[i]=rt; q.push(i); } } while(!q.empty()){ int u=q.front();q.pop(); dep[u]=dep[f[u]]+1; Insert2(f[u],u); anc[u][0]=f[u]; for(int i=1;i ==== 一般有向图 ==== 挖坑待填 ===== 算法例题 ===== ==== 例题一 ==== [[http://codeforces.com/gym/101741/problem/L|gym 101741 L]] === 题意 === 给定一个无向连通图,定义源点为 $1$ 号点。对每条边,询问删除该边会导致源点到多少个点的最短路改变。 === 题解 === 首先跑最短路,然后保留所有在最短路树上的边,同时规定每条边方向由距离近的点指向距离远的点,易知构成有向无环图。 问题转化为求有向无环图的支配边。 对任意一个点,如果该点有至少两条入边,易知所有入边支配点集均为空,否则该边的支配点集等价于该点的支配子树。 const int MAXN=2e5+5,MAXM=2e5+5,MAXV=22; namespace Tree{ struct Edge{ int to,id,next; }edge[MAXN+MAXM]; int head1[MAXN],head2[MAXN],edge_cnt; int deg[MAXN],f[MAXN],dep[MAXN],anc[MAXN][MAXV],lg2[MAXN]; int deg0[MAXN]; void Insert1(int u,int v,int id){ edge[++edge_cnt]=Edge{v,id,head1[u]}; head1[u]=edge_cnt; deg[v]++; deg0[v]++; } void Insert2(int u,int v){ edge[++edge_cnt]=Edge{v,0,head2[u]}; head2[u]=edge_cnt; } int LCA(int u,int v){ if(dep[u]dep[v])u=anc[u][lg2[dep[u]-dep[v]]]; if(u==v) return u; for(int i=MAXV-1;i>=0;i--){ if(anc[u][i]!=anc[v][i]) u=anc[u][i],v=anc[v][i]; } return anc[u][0]; } int build(int n){ lg2[1]=0; _for(i,2,MAXN)lg2[i]=lg2[i>>1]+1; int rt=n+1; queue q; _rep(i,1,n){ if(deg[i]==0){ f[i]=rt; q.push(i); } } while(!q.empty()){ int u=q.front();q.pop(); dep[u]=dep[f[u]]+1; Insert2(f[u],u); anc[u][0]=f[u]; for(int i=1;i >q; mem(dis,127); dis[1]=0; q.push(make_pair(-dis[1],1)); while(!q.empty()){ int u=q.top().second; q.pop(); if(vis[u])continue; vis[u]=true; for(int i=head[u];i;i=edge[i].next){ int v=edge[i].to; if(dis[v]>dis[u]+edge[i].w){ dis[v]=dis[u]+edge[i].w; q.push(make_pair(-dis[v],v)); } } } _rep(u,1,n){ for(int i=head[u];i;i=edge[i].next){ int v=edge[i].to; if(dis[v]==dis[u]+edge[i].w) Tree::Insert1(u,v,edge[i].id); } } Tree::solve(n,m); return 0; }