老师讲图论-缩点-复习
我想 我没学过 缩点啊 \(OTL\)
先讲 割点
定义 在无向连通图中,如果将其中一个点以及所有连接该点的边去掉,图就不再连通,那么这个点就叫做割点
主要思想
观察\(DFS\)搜索树,我们可以发现有两类节点可以成为割点:
对根节点\(u\),若其有两棵或两棵以上的子树,则该根结点u为割点;
对非叶子节点\(u\)(非根节点),若其中的某棵子树的节点均没有指向u的祖先节点的回边,说明删除u之后,根结点与该棵子树的节点不再连通;则节点u为割点。
我们用\(dfn[u]\)记录节点u在DFS过程中被遍历到的次序号,\(low[u]\)记录节点\(u\)或\(u\)的子树通过非父子边追溯到最早的祖先节点(即\(DFS\)次序号最小),那么\(low[u]\)的计算过程如下:
#include <cstdio> #include <vector> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define reg register int #define isdigit(x) ('0' <= (x)&&(x) <= '9') template<typename T> inline T Read(T Type) { T x = 0,f = 1; char a = getchar(); while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();} while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();} return x * f; } const int MAXN = 20010,MAXM = 200010; bool vis[MAXN],out[MAXN]; int head[MAXN],cnt,dfn[MAXN],low[MAXN],fa[MAXN],tot,root; struct node { int v,next; }edge[MAXM]; inline void addedge(int u,int v) { edge[++cnt].v = v; edge[cnt].next = head[u]; head[u] = cnt; } inline void Tarjan(int x) { vis[x] = 1,dfn[x] = low[x] = ++tot; int kid = 0; for(reg i = head[x];i;i = edge[i].next) { int v = edge[i].v; if(!vis[v]) { Tarjan(v); if(x == root)kid++; fa[v] = x; low[x] = min(low[x],low[v]); if(x != root&&low[v] >= dfn[x]) out[x] = 1; } low[x] = min(low[x],dfn[v]); } if(x == root&&kid >= 2) out[x] = 1; } int main() { int n = Read(1),m = Read(1); for(reg i = 1;i <= m;i++) { int u = Read(1),v = Read(1); addedge(u,v),addedge(v,u); } for(reg i = 1;i <= n;i++) if(!vis[i]) { root = i; Tarjan(i); } int size = 0; for(reg i = 1;i <= n;i++) if(out[i]) size++; printf("%d\n",size); for(reg i = 1;i <= n;i++) if(out[i]) printf("%d ",i); return 0; }
缩点
就将强连通分量的点 缩成一个点
补充:\(DAG\) 有向无环图
\(Tarjan\)缩点\(+DAGdp\)
\(DAGdp\)的\(dp\)
如果是这样的\(dp[son]=f(dp[fa])\)
先要拓扑排序
#include <stack> #include <queue> #include <cstdio> #include <cstring> #include <iostream> using namespace std; #define reg register int #define isdigit(x) ('0' <= x&&x <= '9') template<typename T> inline T Read(T Type) { T x = 0,f = 1; char a = getchar(); while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();} while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();} return x * f; } const int MAXN = 1e4 + 10,MAXM = 1e5 + 10; bool vis[MAXN],instack[MAXN]; stack<int> st; vector<int> seq,ahead[MAXN],behind[MAXN]; int n,cnt,d,cnt_scc,ans,dp[MAXN],head[MAXN],tot; struct point { int scc,dfn,low,power; }poi[MAXN]; struct SCC { int power,In; }scc[MAXN]; struct node { int u,v,next; }edge[MAXM]; inline void addedge(int u,int v) { edge[++cnt].v = v; edge[cnt].u = u; edge[cnt].next = head[u]; head[u] = cnt; } inline void tarjan(int x) { poi[x].dfn = poi[x].low = ++tot; vis[x] = 1,st.push(x),instack[x] = 1; for(reg i = head[x];i;i = edge[i].next) { int v = edge[i].v; if(!vis[v]) { tarjan(v); poi[x].low = min(poi[x].low,poi[v].low); } else if(instack[v]) poi[x].low = min(poi[x].low,poi[v].dfn); } if(poi[x].dfn == poi[x].low) { scc[++cnt_scc].power = scc[++cnt_scc].In = 0; while(1) { int t = st.top();st.pop(); poi[t].scc = cnt_scc;instack[t] = 0; scc[cnt_scc].power += poi[t].power; if(t == x) break; } } return; } inline void topo() { queue<int> q; for(reg i = 1;i <= cnt_scc;i++) if(!scc[i].In) q.push(i); while(!q.empty()) { int it = q.front();q.pop(); seq.push_back(it); for(reg i = 0;i < behind[it].size();i++) { int v = behind[it][i]; scc[v].In--; if(!scc[v].In) q.push(v); } } } int main() { n = Read(1);int m = Read(1); for(reg i = 1;i <= n;i++) poi[i].power = Read(1); for(reg i = 1;i <= m;i++) { int u = Read(1),v = Read(1); addedge(u,v); } for(reg i = 1;i <= n;i++) if(!poi[i].dfn) tarjan(i); for(reg i = 1;i <= m;i++) { int u = edge[i].u,v = edge[i].v; if(poi[u].scc != poi[v].scc) { int sccu = poi[u].scc,sccv = poi[v].scc; scc[sccv].In++,behind[sccu].push_back(sccv),ahead[sccv].push_back(sccu); } } topo(); for(reg i = 0;i < seq.size();i++) { int x = seq[i]; dp[x] = scc[x].power; for(reg j = 0;j < ahead[x].size();j++) dp[x] = max(dp[ahead[x][j]] + scc[x].power,dp[x]); ans = max(ans,dp[x]); } printf("%d\n",ans); return 0; }
\(Tarjan\)缩点后 求入度为零的点の数
#include <stack> #include <queue> #include <cstdio> #include <cstring> #include <iostream> using namespace std; #define reg register int #define isdigit(x) ('0' <= x&&x <= '9') template<typename T> inline T Read(T Type) { T x = 0,f = 1; char a = getchar(); while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();} while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();} return x * f; } const int MAXN = 1e5 + 10,MAXM = 5e5 + 10; bool vis[MAXN],instack[MAXN]; stack<int> st; vector<int> seq,ahead[MAXN],behind[MAXN]; int n,cnt,d,cnt_scc,ans,dp[MAXN],head[MAXN],tot; struct point { int scc,dfn,low; }poi[MAXN]; struct SCC { int In; }scc[MAXN]; struct node { int u,v,next; }edge[MAXM]; inline void addedge(int u,int v) { edge[++cnt].v = v; edge[cnt].u = u; edge[cnt].next = head[u]; head[u] = cnt; } inline void tarjan(int x) { poi[x].dfn = poi[x].low = ++tot; vis[x] = 1,st.push(x),instack[x] = 1; for(reg i = head[x];i;i = edge[i].next) { int v = edge[i].v; if(!vis[v]) { tarjan(v); poi[x].low = min(poi[x].low,poi[v].low); } else if(instack[v]) poi[x].low = min(poi[x].low,poi[v].dfn); } if(poi[x].dfn == poi[x].low) { scc[++cnt_scc].In = 0; while(1) { int t = st.top();st.pop(); poi[t].scc = cnt_scc;instack[t] = 0; if(t == x) break; } } return; } int main() { n = Read(1);int m = Read(1); for(reg i = 1;i <= m;i++) { int u = Read(1),v = Read(1); addedge(u,v); } for(reg i = 1;i <= n;i++) if(!poi[i].dfn) tarjan(i); for(reg i = 1;i <= m;i++) { int u = edge[i].u,v = edge[i].v; if(poi[u].scc != poi[v].scc) { int sccu = poi[u].scc,sccv = poi[v].scc; scc[sccv].In++,behind[sccu].push_back(sccv),ahead[sccv].push_back(sccu); } } for(reg i = 1;i <= cnt_scc;i++) ans += !scc[i].In; printf("%d\n",ans); return 0; }
先判是否有解 \(DFS\)
再\(Tarjan\)缩点后,求入读为零的点的权值之和最小(缩点后的点的权值保持前面的点权最小值)
#include <stack> #include <queue> #include <cstdio> #include <cstring> #include <iostream> using namespace std; #define reg register int #define isdigit(x) ('0' <= x&&x <= '9') template<typename T> inline T Read(T Type) { T x = 0,f = 1; char a = getchar(); while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();} while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();} return x * f; } const int MAXN = 1e5 + 10,MAXM = 5e5 + 10; bool vis[MAXN],esp[MAXN],instack[MAXN]; stack<int> st; int area,n,cnt,cnt_scc,ans,head[MAXN],tot; struct point {int scc,dfn,low,cost;} poi[MAXN]; struct SCC {int In,cost;} scc[MAXN]; struct node {int u,v,next;} edge[MAXM]; inline void addedge(int u,int v) { edge[++cnt].v = v; edge[cnt].u = u; edge[cnt].next = head[u]; head[u] = cnt; } inline void dfs(int x) { vis[x] = 1; ++area; for(reg i = head[x];i;i = edge[i].next) { int v = edge[i].v; if(!vis[v]) dfs(v); } return; } inline void tarjan(int x) { poi[x].dfn = poi[x].low = ++tot; vis[x] = 1,st.push(x),instack[x] = 1; for(reg i = head[x];i;i = edge[i].next) { int v = edge[i].v; if(!vis[v]) { tarjan(v); poi[x].low = min(poi[x].low,poi[v].low); } else if(instack[v]) poi[x].low = min(poi[x].low,poi[v].dfn); } if(poi[x].dfn == poi[x].low) { ++cnt_scc; while(1) { int t = st.top();st.pop(); scc[cnt_scc].cost = min(scc[cnt_scc].cost,poi[t].cost); poi[t].scc = cnt_scc,instack[t] = 0; if(t == x) break; } } return; } int main() { memset(scc,0,sizeof(scc)); n = Read(1); int p = Read(1),m; for(reg i = 1;i <= n;i++) scc[i].cost = poi[i].cost = MAXN; for(reg i = 1;i <= p;i++) { int k; poi[k = Read(1)].cost = Read(1); esp[k] = 1; } m = Read(1); for(reg i = 1;i <= m;i++) { int u = Read(1),v = Read(1); if(u == v) continue; addedge(u,v); } for(reg i = 1;i <= n;i++) if(esp[i]) dfs(i); if(area < n) { printf("NO\n"); for(reg i = 1;i <= n;i++) if(!vis[i]) { printf("%d",i); return 0; } } memset(vis,0,sizeof(vis)); for(reg i = 1;i <= n;i++) if(!poi[i].dfn) tarjan(i); for(reg i = 1;i <= m;i++) { int u = edge[i].u,v = edge[i].v; if(poi[u].scc != poi[v].scc) { int sccu = poi[u].scc,sccv = poi[v].scc; scc[sccv].In++; } } for(reg i = 1;i <= cnt_scc;i++) if(!scc[i].In) ans += scc[i].cost; printf("YES\n%d\n",ans); return 0; }