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

「图论」第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;
} 
posted @ 2021-05-04 08:09  追梦人1024  阅读(52)  评论(0编辑  收藏  举报