首页 > 其他分享 >Living-Dream 系列笔记 第46期

Living-Dream 系列笔记 第46期

时间:2024-03-02 16:59:22浏览次数:18  
标签:Living 46 scc tot int dfn low Dream instk

强连通分量(Strongly Connected ComponentsSCC)。

强连通:有向图中,\(x,y\) 能相互到达。

弱连通:有向图中,\(x\) 能到 \(y\),\(y\) 不能到 \(x\)(或反之)。

强连通分量:有向图 \(G\) 中一极大子图 \(G1\),使得 \(G1\) 任意两点均强连通,且 \(G1\) 不可变得更大(不能添加点)。

强连通分量要么是单点,要么是(不一定是简单环,即有可能嵌套环)。

有向图中一定存在强连通分量。

Tarjan 算法(由 Robert Tarjan 发明):

  • \(\text{dfn}\) 表示 dfs 搜索时每个点的时间戳(即 dfs)。

  • \(\text{low}\) 表示每个点能到达的点的最小时间戳。

  • 关键点 表示首次进入环的点,其中关键点的 \(\text{dfn = low}\)。

  • \(\text{dfn}\) 在 dfs 时记录即可。

  • \(\text{low}\) 在回溯时取 \(\min\) 即可。

T1

板子。

#include<bits/stdc++.h>
using namespace std;

const int N=1e4+5,M=1e5+5;
int n,m,cnt,tot;
int dfn[N],low[N],scc[N];
vector<int> scc_id[N];
bool instk[N];
vector<int> G[M];
stack<int> s;

void tarjan(int v){
	s.push(v),instk[v]=1,dfn[v]=low[v]=++cnt;
	for(int i:G[v]){
		if(!dfn[i]) tarjan(i),low[v]=min(low[v],low[i]);
		else if(instk[i]) low[v]=min(low[v],dfn[i]);
	}
	if(dfn[v]==low[v]){
		++tot;
		for(;s.top()!=v;s.pop())
			instk[s.top()]=0,scc[s.top()]=tot,scc_id[tot].push_back(s.top());
		instk[v]=0,scc[v]=tot,scc_id[tot].push_back(v),s.pop();
	}
}

int main(){
	cin>>n>>m;
	for(int i=1,u,v;i<=m;i++)
		cin>>u>>v,
		G[u].push_back(v);
	for(int i=1;i<=n;i++)
		if(!dfn[i]) tarjan(i);
	cout<<tot<<'\n';
	for(int i=1;i<=n;i++){
		if(!dfn[i]) continue;
		sort(scc_id[scc[i]].begin(),scc_id[scc[i]].end());
		for(int j:scc_id[scc[i]]) cout<<j<<' ',dfn[j]=0;
		cout<<'\n';
	}
	return 0;
}

T2

Tarjan 缩点后 topo 进行 dp 转移即可。

#include<bits/stdc++.h>
using namespace std;

const int N=1e4+5,M=1e5+5;
int n,m,ans=-1e9;
int p,cnt,tot;
int a[N];
int in[N],dp[N];
int dfn[N],low[N];
int scc[N],sum[N];
bool instk[N];
struct Edge{ int u,v,w; }e[M];
vector<int> G[M];
vector<int> V[M];
stack<int> s;

void tarjan(int v){
	s.push(v),instk[v]=1,dfn[v]=low[v]=++cnt;
	for(int i:G[v]){
		if(!dfn[i]) tarjan(i),low[v]=min(low[v],low[i]);
		else if(instk[i]) low[v]=min(low[v],dfn[i]);
	}
	if(dfn[v]==low[v]){
		++tot;
		for(;s.top()!=v;s.pop()) instk[s.top()]=0,scc[s.top()]=tot,sum[tot]+=a[s.top()];
		instk[v]=0,scc[v]=tot,sum[tot]+=a[v],s.pop();
	}
}
void topo(){
	queue<int> q;
	for(int i=1;i<=tot;i++)
		if(!in[i]) q.push(i),dp[i]=sum[i];
	while(!q.empty()){
		int now=q.front(); q.pop();
		for(int i:V[now]){
			--in[i],dp[i]=max(dp[i],dp[now]+sum[i]);
			if(!in[i]) q.push(i);
		}
	}
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1,u,v;i<=m;i++) cin>>u>>v,G[u].push_back(v),e[++p]={u,v};
	for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
	for(int i=1;i<=m;i++){
		int x=scc[e[i].u],y=scc[e[i].v];
		if(x!=y) in[y]++,V[x].push_back(y);
	}
	topo();
	for(int i=1;i<=tot;i++) ans=max(ans,dp[i]);
	cout<<ans;
	return 0;
}

T3

首先还是 Tarjan 缩点,

然后新建图时记录出度,

若某个强连通分量出度为 \(0\),

则这个强连通分量的点数即为答案。

同时,若多个强连通分量出度为 \(0\),

则答案为 \(0\)。

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=1e4+5,M=5e4+5;
int n,m,ans;
int p,q,cnt,tot;
int /*in[N],*/out[N],dp[N];
int dfn[N],low[N];
int scc[N],sz[N];
bool instk[N];
struct Edge{ int u,v; }e[M];
vector<int> G[M];
vector<int> V[M];
stack<int> s;

void tarjan(int v){
	s.push(v),instk[v]=1,dfn[v]=low[v]=++cnt;
	for(int i:G[v]){
		if(!dfn[i]) tarjan(i),low[v]=min(low[v],low[i]);
		else if(instk[i]) low[v]=min(low[v],dfn[i]);
	}
	if(dfn[v]==low[v]){
		++tot;
		for(;s.top()!=v;s.pop()) instk[s.top()]=0,scc[s.top()]=tot,sz[tot]++;
		instk[v]=0,scc[v]=tot,sz[tot]++,s.pop();
	}
}
/*
void topo(){
	queue<int> q;
	for(int i=1;i<=tot;i++)
		if(!in[i]) q.push(i),dp[i]=1;
	while(!q.empty()){
		int now=q.front(); q.pop();
		for(int i:V[now]){
			--in[i],dp[i]=max(dp[i],dp[now]+1);
			if(!in[i]) q.push(i);
		}
	}
}
*/

signed main(){
	cin>>n>>m;
	for(int i=1,u,v;i<=m;i++) cin>>u>>v,G[u].push_back(v),e[++p]={u,v};
	for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
	//cout<<tot<<'\n';
	//for(int i=1;i<=tot;i++) cout<<sz[i]<<'\n';
	for(int i=1;i<=m;i++){
		int x=scc[e[i].u],y=scc[e[i].v];
		if(x!=y) V[x].push_back(y),out[x]++;
	}
	//topo();
	for(int i=1;i<=tot;i++)
		//if(dp[i]==tot) ans+=sz[i];
		if(!out[i]) ans=sz[i],q++;
	cout<<(q==1?ans:0);
	return 0;
}

标签:Living,46,scc,tot,int,dfn,low,Dream,instk
From: https://www.cnblogs.com/XOF-0-0/p/18048817

相关文章

  • Living-Dream 系列笔记 第43期
    bellman-ford:因为最短路最多\(n\)点\(n-1\)边,则进行\(n-1\)轮操作,每轮枚举\(m\)边进行松弛即可。时间复杂度\(O(nm)\)。spfa:正确的称呼是队列优化的bellman-ford。我们知道,对于一个点,只有它被松弛了,它的邻接点才有可能被松弛。于是我们用队列记录可能被松弛的点,每......
  • Living-Dream 系列笔记 第45期
    负环,即负权环,指在图\(G\)中边权和为负数的一回路。负环的判定一般有两种方式。以下均以\(n\)点\(m\)边的图\(G\)为例。法一:以边为研究对象。注意到最短路边数一定不超过\(n-1\)边,因此维护\(cnt_x\)表示起点到\(x\)的边数,若某一时刻存在\(cnt_x>n-1\)则存在......
  • Living-Dream 系列笔记 第42期
    T1枚举流量对于花费跑dijkstra并取比值的\(\max\)即可。关于为什么枚举流量不一定当前最优的问题,因为最优解的流量总在枚举范围内,所以无需考虑当前是否最优。#include<bits/stdc++.h>usingnamespacestd;intn,m,ans;intdis[100031];boolvis[100031];structEdge{......
  • Living-Dream 系列笔记 第44期
    比赛链接T1\(100\to10\)。错因:01背包转移方程写错,没有取\(\max\)。并查集合并错写成将u,v而非fnd(u),fnd(v)合并。#include<bits/stdc++.h>usingnamespacestd;intn,m,w;intc[10031],d[10031];intfa[10031];intdp[10031];intfnd(intx){return......
  • Living-Dream 系列笔记 第41期
    稍微讲一下dijkstraqwq。dijkstra、bellman-ford(orspfa)、bfs的区别:dijkstra以点为研究对象;bellman-ford/spfa则以边为研究对象;而bfs可以看作边权为\(1\)(or全都相同)的dijkstra,因此它可以用队列实现。dijkstra的具体实现:将所有点分为两类(黑点/白点),......
  • Living-Dream 系列笔记 第40期
    T1bf的做法是\(n\)次floyd,实测可以卡过。然后我们发现当点\(u\)为重要点时,当且仅当存在\((a,b)\)使得\(u\)为它们的唯一中转点。于是我们令\(vis_{i,j}\)表示\((i,j)\)的唯一中转点,接着在floyd的松弛操作中若能松弛则更新其为当前中转点\(k\),否则若没有更优......
  • Living-Dream 系列笔记 第39期
    T1一头牛能确定关系,当且仅当它能到达/被到达除自己外的所有牛。于是我们建出有向图,跑floyd传递闭包,然后对于每个点统计他能到达的点数是否为\(n-1\)即可。#include<bits/stdc++.h>usingnamespacestd;intn,m,ans;intdp[131][131];vector<int>G[4531];voidfl......
  • SP14846 GCJ1C09C - Bribe the Prisoners 题解
    非常好区间dp。我们发现直接依题做是困难的,因此考虑反着做。也即,假定起初那\(Q\)个牢房均为空,现在要将给定的\(Q\)的犯人插入其中,求最小代价。然后我们发现这题和P1775很像,相当于每插入一个人,两段不相邻的牢房就被合并到了一起。接着我们就考虑这玩意怎么做区间dp。......
  • Living-Dream 周考总结 第3期
    Link,第四场没打。T1\(100\),没挂分。\(\operatorname{lcm}(x,y)=\dfrac{x}{\gcd(x,y)}\timesy\)#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;signedmain(){ //freopen("as01.in","r",stdin); //freopen("as0......
  • Living-Dream 周考总结 第4期
    Link。T1\(100\),没挂分。依题计算即可。#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;signedmain(){ //freopen("as01.in","r",stdin); //freopen("as01.out","w",stdout); ios::sync_with_stdio(0); ......