ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

「图论」第4章 强连通分量课堂过关

2021-05-04 08:33:10  阅读:158  来源: 互联网

标签:图论 过关 int top 连通 while ge low include


目录

「图论」第4章 强连通分量课堂过关

A. 【例题1】有向图缩点

题目

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
#define N 10010
#define M 100010
int read() {
	int re = 0;
	bool sig = 0;
	char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') 
			sig = 1;
		c = getchar();
	}
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0' , c = getchar();
	return sig ? -re : re;
}
struct node {
	int to , nxt;
}ed[2][M];
int head[2][N];
void addedge(int id , int u , int v) {
	static int cnt[2];
	node *e = ed[id];
	int *h = head[id];
	int c = ++cnt[id];
	e[c].to = v , e[c].nxt = h[u] , h[u] = c;
	return;
}

int n , m;
int val[N];
int dfn[N] , low[N]; 
int stack[N] , top;
int col[N];
int sum[N] ;
int tot;

int maxv[N];
int ind[N];
void Tarjan(int x) {
	static int Time = 0;
	dfn[x] = low[x] = ++Time;
	stack[++top] = x;
	for(int i = head[0][x] ; i ; i = ed[0][i].nxt) {
		int to = ed[0][i].to;
		if(!dfn[to]) {
			Tarjan(to);
			if(low[to] < low[x])
				low[x] = low[to];
		}
		else if(col[to] == 0 && low[x] > low[to])
			low[x] = low[to];
	}
	if(dfn[x] == low[x]) {
		col[x] = ++tot;
		sum[tot] += val[x];
		while(stack[top] != x)
			sum[tot] += val[stack[top]] , col[stack[top]] = tot , --top;
		top--;
	}
}
int main() {
	n = read() , m = read();
	for(int i = 1 ; i <= n ; i++)
		val[i] = read();
	for(int i = 1 ; i <= m ; i++) {
		int u = read() , v = read();
		addedge(0 , u , v);
	}
	
	for(int i = 1 ; i <= n ; i++)
		if(dfn[i] == 0)
			Tarjan(i);
	
	for(int i = 1 ; i <= n ; i++)
		for(int j = head[0][i] ; j ; j = ed[0][j].nxt) {
			int u = col[i] , v = col[ed[0][j].to];
			if(u != v) {
				addedge(1 , u , v);
				++ind[v];
			}
		}
	
	queue <int> q;
	for(int i = 1 ; i <= tot ; i++)
		if(ind[i] == 0){
			maxv[i] = sum[i];
			q.push(i);
		}	
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		for(int i= head[1][u] ; i ; i = ed[1][i].nxt) {
			int v = ed[1][i].to;
			if(maxv[u] + sum[v] > sum[v])
				sum[v] = maxv[u] + sum[v];
			--ind[v];
			if(ind[u] == 0)
				q.push(v);
		}
	}
	int ans = 0;
	for(int i = 1 ; i <= tot ; i++)
		if(ans < maxv[i])
			ans = maxv[i];
	cout << ans; 
	return 0;
}

B. 【例题2】受欢迎的牛

题目

思路

按喜欢关系建图,跑一边tarjan,若生成的DAG中同时存在多个出度为0的点,则这些点一定不能互相到达,答案为0,否则,出度为0的点代表的原图强连通块的点即为明星.

代码

#include <iostream>
#include <cstdio>
#define N 10010
#define M 100010
using namespace std;
int read() {
	int re = 0;
	bool sig = 0;
	char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') 
			sig = 1;
		c = getchar();
	}
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0' , c = getchar();
	return sig ? -re : re;
}

struct node{
	int to , nxt;
}ed[M];
int head[N];
void addedge(int u , int v) {
	static int cnt = 0;
	++cnt;
	ed[cnt].nxt = head[u] , ed[cnt].to = v , head[u] = cnt;
}

int n , m;
int n2;
int dfn[N] , low[N];
int col[N] , w[N];
int stac[N] , top;
void tarjan(int x) {
	static int Time = 0;
	stac[++top] = x;
	dfn[x] = low[x] = ++Time;
	for(int i = head[x] ; i ; i = ed[i].nxt) {
		int to = ed[i].to;
		if(dfn[to] == 0) {
			tarjan(to);
			if(low[to] < low[x])
				low[x] = low[to];
		}
		else if(col[to] == 0)
			if(low[to] < low[x])
				low[x] = low[to];
	}
	if(dfn[x] == low[x]) {
		++n2;
		col[x] = n2;
		++w[n2];
		while(stac[top] != x) {
			col[stac[top]] = n2;
			++w[n2];
			--top;
		}
		--top;
	}
}
int out[N];
int main() {
	n = read();
	m = read();
	for(int i = 1 ; i <= m ; i++) {
		int u = read() , v = read();
		addedge(u , v);
	}
	for(int i = 1 ; i <= n ; i++) 
		if(dfn[i] == 0)
			tarjan(i);
	for(int i = 1 ; i <= n ; i++)
		for(int j = head[i] ; j ; j = ed[j].nxt) {
			int u = col[i] , v = col[ed[j].to];
			if(u == v)continue;
			++out[u];
		}
	int k = -1;
	for(int i = 1 ; i <= n2 ; i++) {
		if(out[i] == 0) {
			if(k == -1)	k = i;
			else {
				cout << 0;
				return 0;
			}
		}
	}
	cout << w[k];
	return 0;
} 

D. 【例题4】恒星的亮度

题目

思路

首先,这题用到差分约束,没学的先学

鉴于恒星亮度为整数,我们转换一下题目所给的关系:

\[\begin{cases} A=B\qquad\qquad &A\ge B \and B\ge A\\ A < B &B \ge A+1\\ A \ge B & A \ge B\\ A > B & A \ge B+1\\ A \le B & B \ge A \end{cases} \]

这样,原关系就只剩下\(\ge\)了.根据差分约束思想,若\(A\ge B+val\)就从\(B\)到\(A\)连一条权值为\(val\),从每个入度为0的点出发跑一边最长路,距离即亮度, 若出现正环,则说明有\(A\ge B ,B\ge C , C > A\)的错误关系,输出-1.

问题是,怎么判断正环呢?SPFA?必TLE!

看到这题边权只有0,1两种情况,不难想到,当一个强连通块内存在一条正权边时,必有正环,输出-1

剩下的,交给拓扑DP即可

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <cstring>
using namespace std;
int read() {
	int re = 0;
	char c = getchar();
	while(c < '0' || c > '9') c = getchar();
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0',
		c = getchar();
	return re;
}
#define N 100010
#define M 200010
struct node {
	int to , nxt , val;
}ed[M * 2];
int HEAD[2][N];
int *head;

void addedge(int u , int v , int val) {
	static int cnt = 0;
	++cnt;
	ed[cnt].nxt = head[u] , ed[cnt].to = v , ed[cnt].val = val , head[u] = cnt;
}

int n , m , n2;
int dfn[N] , low[N] , col[N] , w[N];
int stac[N] , top;
int dist[N];
void tarjan(int x) {
	static int Time = 0;
	dfn[x] = low[x] = ++Time;
	stac[++top] = x;
	for(int j = 1 ; j >= 0 ; j--)
		for(int i = head[x] ; i ; i = ed[i].nxt) {
			if(ed[j].val != j)	continue;
			int to = ed[i].to;
			if(dfn[to] == 0) {
				tarjan(to );
				if(low[to] < low[x])
					low[x] = low[to];
			}
			else if(col[to] == 0) {
				if(low[to] < low[x])
					low[x] = low[to];
			}
		}
	if(dfn[x] == low[x]) {
		col[x] = ++n2;
		while(x != stac[top]) {
			col[stac[top]] = n2;
			--top;
		}
		--top;
	}
}

int ind[N];
int main() {
	n = read() , m = read();
	head = HEAD[0];
	for(int i = 1 ; i <= m ; i++) {
		int T = read();
		int u = read() , v = read();
		switch(T) {
			case 1 :
				addedge(u , v , 0);
				addedge(v , u , 0);
				break;
			case 2 :
				addedge(u , v , 1); 
				break;
			case 3 :
				addedge(v , u , 0);
				break;
			case 4 :
				addedge(v , u , 1);
				break;
			case 5 :
				addedge(u , v , 0);
				break;
		}
	}/*
	for(int i = 1 ; i <= n ; i++) {
		cout << i << ":\t";
		for(int j = head[i] ; j ; j = ed[j].nxt)
			cout << ed[j].to << ',' << ed[j].val << ' ';
		cout << endl;
	}*/
	for(int i = 1 ; i <= n ; i++)
		if(dfn[i] == 0)
			tarjan(i);
	head = HEAD[1];
	for(int i = 1 ; i <= n ; i++)
		for(int j = HEAD[0][i] ; j ; j = ed[j].nxt) {
			int u = col[i] , v = col[ed[j].to];
			if(u == v) {
				if(ed[j].val > 0) {
					cout << -1;
					return 0;
				}
				continue;
			} 
			++ind[v];
			addedge(u , v , ed[j].val);
		}
		
	queue <int> q;
	memset(dist , 0 , sizeof(dist));
	for(int i = 1 ; i <= n2 ; i++)
		if(ind[i] == 0) {
			dist[i] = 1;
			q.push(i);
		}
	while(!q.empty()) {
		int k = q.front();
		q.pop();
		for(int i = head[k] ; i ; i = ed[i].nxt) {
			int to = ed[i].to;
			if(--ind[to] == 0)
				q.push(to);
			if(dist[k] + ed[i].val > dist[to])
				dist[to] = dist[k] + ed[i].val;
		}
	}
	long long ans = 0;
	for(int i = 1 ; i <= n ; i++)
		ans += dist[col[i]];
	cout << ans;
	return 0;
} 

标签:图论,过关,int,top,连通,while,ge,low,include
来源: https://www.cnblogs.com/dream1024/p/14728804.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有