免费网站推广网站在线,西双版纳傣族自治州天气,优化营商环境条例心得体会,来宾住房与城乡建设网站定义与声明
一个有向图 GGG。给定一个起点 sss#xff0c;假设 sss 能到达所有点。
若去掉某个点 iii 后#xff0c;sss 无法到达 jjj#xff0c;则称 iii 为 jjj 的支配点。
显然支配点存在传递关系。
以 sss 为根#xff0c;使得对于任意节点 iii#xff0c;其树上祖…定义与声明
一个有向图 GGG。给定一个起点 sss假设 sss 能到达所有点。
若去掉某个点 iii 后sss 无法到达 jjj则称 iii 为 jjj 的支配点。
显然支配点存在传递关系。
以 sss 为根使得对于任意节点 iii其树上祖先均为 iii 的支配点这样的树称之为支配树。有的人叫做灭绝树
任一有向图都存在支配树前提是满足 sss 能到达所有点。
支配树不一定是图 GGG 的树形图即树上有些边不存在于图 GGG 中。 dfs\text{dfs}dfs 树。
对图 GGG 建 dfs\text{dfs}dfs 树。每个点有一个 dfn\text{dfn}dfn 序即 dfs\text{dfs}dfs 的时间戳。
dfs\text{dfs}dfs 树中的边称为树边除此之外的原图中的边称为非树边。
非树边又分为前向边返祖边横叉边。前向边是 dfn\text{dfn}dfn 序小的连向大的返祖边和横叉边则相反。
最近支配点 idom\text{idom}idom。
点 iii 的支配点中 dfn\text{dfn}dfn 序最大的点即支配树上 iii 的父亲。
注意支配树上的父亲不一定就是 dfs\text{dfs}dfs 树上的父亲。
显然除 sss 以外的所有点均有唯一的 idom\text{idom}idom。
半支配点 sdom\text{sdom}sdom。
点 vvv 的半支配点 uuu 是满足『 GGG 中存在一条从点 uuu 到点 vvv 的路径且路径上经过点的 dfndfn[v]\text{dfn}\text{dfn}[v]dfndfn[v]不包含 u,vu,vu,v』的dfn\text{dfn}dfn 最小的点。
即 uuu 只走非树边能到达 vvv 的 dfn\text{dfn}dfn 最小的 uuu。
特别的如果 uuu 有一条边直接连向 vvv则也是 sdom(v)\text{sdom}(v)sdom(v) 的候选点这相当于路径上没有其他点。
u→v:uu\rightarrow v:uu→v:u 存在一条直接连向 vvv 的边。
u⇝^v:u\hat\leadsto v:u⇝^v: 存在一条由树边组成的 uuu 到 vvv 的路径且 u≠vu\ne vuv。
u⇝¨v:u\ddot\leadsto v:u⇝¨v: 存在一条由树边组成的 uuu 到 vvv 的路径允许 uvuvuv。
注意下面引理和定理的树均指 dfs\text{dfs}dfs 树而非支配树。树边/非树边也是在 dfs\text{dfs}dfs 树的基础上定义的。 引理与定理 引理Ⅰ∀i≠s\forall_{i\ne s}∀is 有 idom(i)⇝^i\text{idom}(i)\hat\leadsto iidom(i)⇝^i。idom(i)\text{idom}(i)idom(i) 一定是 dfs\text{dfs}dfs 树上 iii 的某个祖先点。 显然。如果不是那么去掉 idom(i)\text{idom(i)}idom(i) 后sss 可以通过走树边访问到 iii。这就不满足『支配 』的定义了。 引理Ⅱ∀i≠s\forall_{i\ne s}∀is 有 sdom(i)⇝^i\text{sdom}(i)\hat\leadsto isdom(i)⇝^i。sdom(i)\text{sdom(i)}sdom(i) 一定是 dfs\text{dfs}dfs 树上 iii 的某个祖先点。 假设 sdom(i)\text{sdom}(i)sdom(i) 不是 iii 的祖先那么我们可以从 sdom(i)\text{sdom}(i)sdom(i) 开始沿着树边往上走直到走到 iii 的某个祖先点 xxx 上。 这期间肯定不会经过除 xxx 外的任何一个 iii 的祖先点。 显然 xxx 更符合 sdom\text{sdom}sdom 的条件。 引理Ⅲ∀i≠s\forall_{i\ne s}∀is 有 idom(i)⇝¨sdom(i)\text{idom}(i)\ddot\leadsto\text{sdom}(i)idom(i)⇝¨sdom(i)。idom(i)\text{idom}(i)idom(i) 要么是 sdom(i)\text{sdom}(i)sdom(i)要么是 sdom(i)\text{sdom}(i)sdom(i) 的祖先。 通过前面的引理我们知道 idom(i),sdom(i)\text{idom}(i),\text{sdom}(i)idom(i),sdom(i) 一定都在支配树中 iii 到根的路径上即是 iii 的某个祖先点。 所以引理如果不成立我们就可以从 sss 直接走到 sdom(i)\text{sdom(i)}sdom(i) 然后走到 iii且没有经过 idom(i)\text{idom}(i)idom(i)。 这显然不符合 idom(i)\text{idom}(i)idom(i) 的定义。所以引理一定成立。 引理Ⅳ∀u⇝¨v\forall\ u\ddot\leadsto v∀ u⇝¨v 有 u⇝¨idom(v)u\ddot\leadsto \text{idom}(v)u⇝¨idom(v) 或 idom(v)⇝¨idom(u)\text{idom}(v)\ddot\leadsto\text{idom}(u)idom(v)⇝¨idom(u)。 u,v,idom(u),idom(v)u,v,\text{idom}(u),\text{idom}(v)u,v,idom(u),idom(v) 都在根到某个叶子节点的路径上。 引理也就是说这两条 idom(x)\text{idom}(x)idom(x) 到 xxx 的路径完全不交或包含。 分情况讨论 dfn[u]≤dfn[idom(v)]\text{dfn}[u]\le \text{dfn}[\text{idom}(v)]dfn[u]≤dfn[idom(v)]。 此时有 u⇝¨idom(v)⇝¨vu\ddot\leadsto \text{idom(v)}\ddot\leadsto vu⇝¨idom(v)⇝¨v。完全不交。 dfn[u]dfn[idom(v)]\text{dfn}[u]\text{dfn}[\text{idom}(v)]dfn[u]dfn[idom(v)]。 此时有 idom(v)⇝¨u\text{idom}(v)\ddot\leadsto uidom(v)⇝¨u。 然后要么有 idom(u)⇝¨idom(v)\text{idom}(u)\ddot\leadsto \text{idom}(v)idom(u)⇝¨idom(v)要么有 idom(v)⇝¨idom(u)\text{idom}(v)\ddot\leadsto \text{idom}(u)idom(v)⇝¨idom(u)。 如果 idom(u)⇝¨idom(v)\text{idom}(u)\ddot\leadsto \text{idom}(v)idom(u)⇝¨idom(v)那么去掉 idom(v)\text{idom}(v)idom(v)idom(u)\text{idom}(u)idom(u) 一定还能到达 uuu否则 idom(u)\text{idom(u)}idom(u) 就不是 uuu 的支配点而 idom(v)\text{idom}(v)idom(v) 才是了。 所以也一定能到达 vvv。这样又不符合 idom(v)\text{idom}(v)idom(v) 的定义了。矛盾。 所以有 idom(v)⇝¨idom(u)\text{idom}(v)\ddot\leadsto \text{idom}(u)idom(v)⇝¨idom(u)。包含。 定理Ⅰ∀u≠s\forall\ u\ne s∀ us如果 sdom(u)⇝^v⇝¨u∧dfn[sdom(v)]≥dfn[sdom(u)]\text{sdom}(u)\hat\leadsto v\ddot\leadsto u\ \wedge\ \text{dfn}[\text{sdom}(v)]\ge\text{dfn}[\text{sdom}(u)]sdom(u)⇝^v⇝¨u ∧ dfn[sdom(v)]≥dfn[sdom(u)]则有 idom(u)sdom(u)\text{idom}(u)\text{sdom}(u)idom(u)sdom(u)。 前提条件意思就是 对于所有满足 sdom(u)\text{sdom}(u)sdom(u) 是 vvv 祖先vvv 是 uuu 祖先可以相等的 vvv均满足 sdom(u)\text{sdom(u)}sdom(u) 到 uuu 的路径完全包含 sdom(v)\text{sdom}(v)sdom(v) 到 vvv 的路径。 定理Ⅱ∀u≠s\forall\ u\ne s∀ us如果 sdom(u)⇝^v⇝¨u\text{sdom}(u)\hat\leadsto v\ddot\leadsto usdom(u)⇝^v⇝¨u假设 vvv 是 dfn[sdom(v)]\text{dfn}[\text{sdom(v)}]dfn[sdom(v)] 最小的点 且如果 dfn[sdom(v)]dfn[sdom(u)]\text{dfn}[\text{sdom}(v)]\text{dfn}[\text{sdom}(u)]dfn[sdom(v)]dfn[sdom(u)]则有 idom(u)idom(v)\text{idom}(u)\text{idom(v)}idom(u)idom(v)。 结合以上两个定理有推论Ⅰ∀u≠s\forall\ u\ne s∀ us令 uuu 为所有满足 sdom(v)⇝^u⇝¨v\text{sdom}(v)\hat\leadsto u\ddot\leadsto vsdom(v)⇝^u⇝¨v 的 uuu 中 dfn[sdom(u)]\text{dfn}[\text{sdom}(u)]dfn[sdom(u)] 最小的点。 有 idom(v){sdom(v)sdom(u)sdom(v)idom(u)sdom(u)sdom(v)\text{idom}(v)\begin{cases}\text{sdom}(v)\text{sdom}(u)\text{sdom}(v)\\\text{idom}(u)\text{sdom}(u)\text{sdom}(v)\end{cases}idom(v){sdom(v)idom(u)sdom(u)sdom(v)sdom(u)sdom(v)。 定理Ⅲ∀u≠s\forall\ u\ne s∀ us有 sdom(u)min{v∣(v,u)∈E,vu}\text{sdom}(u)\min\Big\{v\mid (v,u)\in E\ ,\ vu\Big\}sdom(u)min{v∣(v,u)∈E , vu} sdom(u)\text{sdom}(u)sdom(u) 的候选点只用考虑两类 由树边或者前向边直接连过来的点。dfn\text{dfn}dfn 更大的且与 uuu 有边相连的点及其祖先的 sdom\text{sdom}sdom。这又可以分为两类 dfn[v]dfn[u],∃(v,u)∈E\text{dfn}[v]\text{dfn}[u],\exist(v,u)\in Edfn[v]dfn[u],∃(v,u)∈E则 sdom(v)\text{sdom}(v)sdom(v) 一定是 sdom(u)\text{sdom}(u)sdom(u) 的一个候选点。满足 dfn[v]dfn[u],∃(v,u)∈E\text{dfn}[v]\text{dfn}[u],\exist(v,u)\in Edfn[v]dfn[u],∃(v,u)∈E 的 vvv 的部分祖先且这些祖先的 dfndfn[u]\text{dfn}\text{dfn}[u]dfndfn[u]则这些祖先的 sdom\text{sdom}sdom 也是候选点。
定理ⅠⅡ实在不会证明感性理解好了直接开始摆烂
大本钟下寄快递上面开摆下面寄 算法步骤
建立支配树的算法步骤 如果对于一棵树树根为 sss那么这棵树本身就是一棵支配树。 如果是一个 DAG\text{DAG}DAG 则可以拓扑排序。然后依次确定每个点的 idom\text{idom}idom。 设当前点为 iii有 j→ij\rightarrow ij→i则所有 jjj 在支配树中的 LCA\text{LCA}LCA 就是 idom(i)\text{idom}(i)idom(i)。 拓扑倍增时间大概 O((nm)logn)O((nm)\log n)O((nm)logn)。 如果是一般图问题则可以先求出每个点的 sdom\text{sdom}sdom然后对所有点连边 (sdom(i),i)(\text{sdom}(i),i)(sdom(i),i)去掉非树边就形成了 DAG\text{DAG}DAG。支配关系与原图一致。 代码实现
DAG\text{DAG}DAG 模板题ZJOI2012 灾难
代码实现
#include bits/stdc.h
using namespace std;
#define maxn 66000
int dep[maxn], deg[maxn], f[maxn][20], siz[maxn];
vector int pre[maxn], G[maxn], rG[maxn];
queue int q;
int n;void dfs( int u, int fa ) {siz[u] 1;for( int v : rG[u] ) if( v ^ fa ) dfs( v, u ), siz[u] siz[v];
}int LCA( int u, int v ) {if( dep[u] dep[v] ) swap( u, v );for( int i 19;~ i;i -- ) if( dep[f[u][i]] dep[v] ) u f[u][i];if( u v ) return u;for( int i 19;~ i;i -- ) if( f[u][i] ^ f[v][i] ) u f[u][i], v f[v][i];return f[u][0];
}int main() {scanf( %d, n );for( int i 1, x;i n;i ) {while( ~ scanf( %d, x ) and x ) //G[x]:x有dfs树路径到的点的集合G[x].push_back( i ), deg[i] ;}int root 0; //新建大起点 即使其变成有根树//pre[i]:i的前驱集合 即从根开始有dfs树路径到i的点for( int i 0;i n;i )if( ! deg[i] ) pre[i].push_back( root ), q.push( i );while( ! q.empty() ) {int u q.front(); q.pop();int lca pre[u][0];for( int x : pre[u] ) lca LCA( lca, x );dep[u] dep[lca] 1;f[u][0] lca;//建立支配树上的关系for( int i 1;i 20;i )f[u][i] f[f[u][i - 1]][i - 1];rG[lca].push_back( u );for( int v : G[u] ) {pre[v].push_back( u );//u是v的前驱之一if( ! -- deg[v] ) q.push( v );}}dfs( root, -1 );for( int i 1;i n;i ) printf( %d\n, siz[i] - 1 );return 0;
}一般有向图模板题luogu-P5180 【模板】支配树
代码实现
#include queue
#include cstdio
#include vector
#include iostream
using namespace std;
#define maxn 200005
struct node {vector int g[maxn];void AddEdge( int u, int v ) {g[u].push_back( v );}
}G, rG, Dfs, rDfs, dominate;//原图 反图 dfs树 反dfs树 支配树
queue int q;
int n, m, cnt;
int id[maxn], dfn[maxn], fa[maxn], anc[maxn], min_anc[maxn], sdom[maxn], d[maxn], dep[maxn], ans[maxn];
int f[maxn][20];
/*
Semi-domination半支配 表示v点的所有半支配点的最小的那个
半支配点
存在从一个点u到v的路径中(不包括uv)所有dfs树的点的dfn都大于v的dfn
如果u是v在dfs树上的父节点那么u也是v的半支配点
*/void dfs( int u ) {//寻找dfs树id[dfn[u] cnt] u; //打时间戳for( int i 0;i G.g[u].size();i ) {int v G.g[u][i];if( dfn[v] ) continue; else;fa[v] u;Dfs.AddEdge( u, v );dfs( v );}
}int find( int x ) {if( x anc[x] ) return x;int father anc[x];anc[x] find( anc[x] );if( dfn[sdom[min_anc[x]]] dfn[sdom[min_anc[father]]] )min_anc[x] min_anc[father];// min_anc表示x到sdom[x]路径上dfn[sdom]值最小的点return anc[x];
}void build_dfs() {//建立与原图等价的DAGfor( int i 1;i n;i )anc[i] min_anc[i] sdom[i] i;for( int i n;i 1;i -- ) {int u id[i];if( ! dfn[u] ) continue;int t n;for( int j 0;j rG.g[u].size();j ) {int v rG.g[u][j];if( dfn[v] dfn[u] )t min( t, dfn[v] );else {find( v );t min( t, dfn[sdom[min_anc[v]]] );}}sdom[u] id[t];anc[u] fa[u];Dfs.AddEdge( sdom[u], u );}
}int lca( int u, int v ) {if( ! u || ! v ) return u v;if( dep[u] dep[v] ) swap( u, v );for( int i 19;~ i;i -- )if( dep[f[u][i]] dep[v] ) u f[u][i];if( u v ) return u;for( int i 19;~ i;i -- )if( f[u][i] ! f[v][i] ) u f[u][i], v f[v][i];return f[u][0];
}void build_dominate( int u ) {int t 0;for( int i 0;i rDfs.g[u].size();i ) {int v rDfs.g[u][i];t lca( t, v );}dep[u] dep[t] 1;dominate.AddEdge( t, u );f[u][0] t;for( int i 1;i 20;i )f[u][i] f[f[u][i - 1]][i - 1];
}void topo() {for( int i 1;i n;i )for( int j 0;j Dfs.g[i].size();j ) {int k Dfs.g[i][j];d[k] ;rDfs.AddEdge( k, i );}for( int i 1;i n;i )if( ! d[i] ) {Dfs.AddEdge( 0, i );rDfs.AddEdge( i, 0 );}q.push( 0 );while( ! q.empty() ) {int u q.front(); q.pop();for( int i 0;i Dfs.g[u].size();i ) {int v Dfs.g[u][i];if( -- d[v] 0 ) {build_dominate( v );q.push( v );}}}
}void work( int u ) {ans[u] 1;for( int i 0;i dominate.g[u].size();i ) {int v dominate.g[u][i];work( v );ans[u] ans[v];}
}int main() {scanf( %d %d, n, m );for( int i 1, u, v;i m;i ) {scanf( %d %d, u, v );G.AddEdge( u, v );rG.AddEdge( v, u );}dfs( 1 );build_dfs();topo();work( 0 );for( int i 1;i n;i )printf( %d , ans[i] );return 0;
}