// // // // // // // // // // // // // //

关于强联通分量

强连通分量与缩点

1. 受欢迎的牛

/*
  Time: 1.31
  Worker: Blank_space
  Source: #10091. 「一本通 3.5 例 1」受欢迎的牛
  强连通分量 缩点 统计入度 
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int FFF = 0x8fffffff;
/*------------------------------------常量定义*/
vector <int> e[A];
int n, m, t, dfn[A], low[A], st[A], cnt, top, d[A], sum, ans, siz[A], out[A];
/*------------------------------------变量定义*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
/*----------------------------------------快读*/
int min(int x, int y) {return x < y ? x : y;}
void tarjan(int u)
{
	dfn[u] = low[u] = ++cnt; st[++top] = u;
	for(int i = 0, lim = e[u].size(); i < lim; i++)
	{
		int v = e[u][i];
		if(!dfn[v]) {tarjan(v); low[u] = min(low[u], low[v]);}
		else if(!d[v]) low[u] = min(low[u], dfn[v]);
	}
	if(dfn[u] == low[u])
	{
		sum++;
		while(st[top] != u) {siz[sum]++; d[st[top--]] = sum;}
		siz[sum]++; d[st[top--]] = sum;
	}
}
/*----------------------------------------函数*/
int main()
{
    n = read(); m = read();
    for(int i = 1; i <= m; i++)
    {
    	int x = read(), y = read();
    	e[x].push_back(y);
    }
    for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
    for(int u = 1; u <= n; u++)
    	for(int i = 0, lim = e[u].size(); i < lim; i++)
    	{
    		int v = e[u][i];
    		if(d[u] != d[v]) out[d[u]]++;
    	}
    for(int i = 1; i <= sum; i++) if(!out[i]){if(t) {puts("0"); return 0;} t = i;}
    printf("%d", siz[t]);
	return 0;
}

2. 最大半连通子图

/*
  Time: 1.31
  Worker: Blank_space
  Source: #10092. 「一本通 3.5 例 2」最大半连通子图
*/
/*--------------------------------------------*/
#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int INF = 0x3f3f3f3f;
const int FFF = 0x8fffffff;
/*------------------------------------常量定义*/
vector <int> e[B], _e[B];
int n, m, mod, dfn[B], vis[B], low[B], siz[B], st[B], top, cnt, sum, d[B], dep[B], f[B], in[B];
bool _vis[B];
queue <int> q;
/*------------------------------------变量定义*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
/*----------------------------------------快读*/
int min(int x, int y) {return x < y ? x : y;}
int max(int x, int y) {return x > y ? x : y;}
void tarjan(int u)
{
	dfn[u] = low[u] = ++cnt; st[++top] = u; _vis[u] = 1;
        for(int i = 0; i < e[u].size(); i++)
	{
		int v = e[u][i];
		if(!dfn[v]) {tarjan(v); low[u] = min(low[u], low[v]);}
		else if(_vis[v]) low[u] = min(low[u], dfn[v]);
	}
	if(low[u] == dfn[u])
	{
		sum++;
		while(st[top] != u) {_vis[st[top]] = 0; siz[sum]++; d[st[top--]] = sum;}
		siz[sum]++; _vis[st[top]] = 0; d[st[top--]] = sum;
	}
}
/*----------------------------------------函数*/
int main()
{
//	freopen("semi2.in", "r", stdin);
    n = read(); m = read(); mod = read();
    for(int i = 1; i <= m; i++)
	{
        int u = read(), v = read();
        e[u].push_back(v);
    }
    for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
    for(int u = 1; u <= n; u++) 
		for(int i = 0; i < e[u].size(); i++)
		{
        	int v = e[u][i];
        	if(d[u] != d[v])
            	_e[d[u]].push_back(d[v]), in[d[v]]++;
    	}
    for(int i = 1; i <= sum; i++) if(!in[i])
        q.push(i), dep[i] = siz[i], f[i] = 1;
    while(!q.empty())
	{
        int u = q.front(); q.pop();
        for(int i = 0; i < _e[u].size(); i++)
		{
            int v = _e[u][i];
            in[v]--;
            if(!in[v]) q.push(v);
            if(vis[v] == u) continue;
            vis[v] = u;
            if(dep[v] < dep[u] + siz[v]) dep[v] = dep[u] + siz[v], f[v] = f[u];
            else if(dep[v] == dep[u] + siz[v]) f[v] = (f[v] + f[u]) % mod;
        }
    }
    int ans = 0, maxdep = 0;
    for(int i = 1; i <= sum; i++)
    	if(maxdep < dep[i])	maxdep = dep[i], ans = f[i];
    	else if(dep[i] == maxdep) ans = (ans + f[i]) % mod;
    printf("%d\n%d\n", maxdep, ans);
	return 0;
}

3. 网络协议

/*
  Time: 1.31
  Worker: Blank_space
  Source: #10093. 「一本通 3.5 练习 1」网络协议
  任务a 强连通分量 缩点 没有入度的点的个数
  任务b 即 使得缩点后的图构成一个强连通分量
  最优方案是从出度为0的点向入度为0的点引边
  边的数量即是入度为0的边的数量与出度为0的边的数量的较大值 
  
  注意特殊情况 可能整个图就是一个强联通分量 
*/
/*--------------------------------------------*/
#include<cstdio>
#include<vector>
using namespace std;
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int FFF = 0x8fffffff;
/*------------------------------------常量定义*/
vector <int> e[110];
int n, m, dfn[110], low[110], st[110], top, cnt, siz[110], in[110], out[110], d[110], sum, sin, sout;
/*------------------------------------变量定义*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
/*----------------------------------------快读*/
int min(int x, int y) {return x < y ? x : y;}
int max(int x, int y) {return x > y ? x : y;}
void tarjan(int u)
{
	dfn[u] = low[u] = ++cnt; st[++top] = u;
	for(int i = 0; i < e[u].size(); i++)
	{
		int v = e[u][i];
		if(!dfn[v]) {tarjan(v); low[u] = min(low[u], low[v]);}
		else if(!d[v]) low[u] = min(low[u], dfn[v]);
	}
	if(low[u] == dfn[u])
	{
		sum++;
		while(st[top] != u) d[st[top--]] = sum;
		d[st[top--]] = sum;
	}
}
/*----------------------------------------函数*/
int main()
{
    n = read();
    for(int i = 1; i <= n; i++)
    	while(scanf("%d", &m) && m)
    		e[i].push_back(m);
    for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
    for(int u = 1; u <= n; u++)
    	for(int i = 0; i < e[u].size(); i++)
    		if(d[u] != d[e[u][i]]) out[d[u]]++, in[d[e[u][i]]]++;
    for(int i = 1; i <= sum; i++)
    {
    	if(!in[i]) sin++;
    	if(!out[i]) sout++;
    }
    if(sum == 1) printf("%d\n0", sin); 
    else printf("%d\n%d", sin, max(sin, sout));
	return 0;
}

4. 消息的传递

/*
  Time: 1.31
  Worker: Blank_space
  Source: #10094. 「一本通 3.5 练习 2」消息的传递
  缩点 入度为0的点的个数 
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std; 
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int FFF = 0x8fffffff;
/*------------------------------------常量定义*/
vector <int> e[1010];
int n, dfn[1010], low[1010], st[1010], top, cnt, d[1010], sum, ans, in[1010];
/*------------------------------------变量定义*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
/*----------------------------------------快读*/
void tarjan(int u)
{
	dfn[u] = low[u] = ++cnt; st[++top] = u;
	for(int i = 0; i < e[u].size(); i++)
	{
		int v = e[u][i];
		if(!dfn[v]) {tarjan(v); low[u] = min(low[u], low[v]);}
		else if(!d[v]) low[u] = min(low[u], dfn[v]);
	}
	if(low[u] == dfn[u])
	{
		sum++;
		while(st[top] != u) d[st[top--]] = sum;
		d[st[top--]] = sum;
	}
}
/*----------------------------------------函数*/
int main()
{
    n = read();
    for(int i = 1; i <= n; i++)
    	for(int j = 1; j <= n; j++)
    	{
    		int x = read();
    		if(x) e[i].push_back(j);
    	}
    for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
    for(int u = 1; u <= n; u++)
    	for(int i = 0; i < e[u].size(); i++)
    	{
    		int v = e[u][i];
    		if(d[u] != d[v]) in[d[v]]++;
    	}
    for(int i = 1; i <= sum; i++) if(!in[i]) ans++;
    printf("%d", ans);
	return 0;
}

5. 间谍网络

/*
  Time: 1.31
  Worker: Blank_space
  Source: #10095. 「一本通 3.5 练习 3」间谍网络
  缩点 缩的过程中维护该强连通分量中能用的点的最小值 
  判断是否能取用所有入度为0的强联通分量以及费用 
  
  注意 输出的点的编号也要是最小的 
  再套一层拓扑试试 
  
  还是锅掉了
  参照题解修码... 
*/
/*--------------------------------------------*/
#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
#define emm(x) memset(x, 0x3f, sizeof x)
using namespace std;
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int FFF = 0x8fffffff;
/*------------------------------------常量定义*/
vector <int> e[3010];
int n, p, r, _ans = INF, dfn[3010], low[3010], st[3010], val[3010], top, cnt, d[3010], a[3010], t[3010], sum, ans, in[3010];
bool flag;
queue <int> q;
/*------------------------------------变量定义*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
/*----------------------------------------快读*/
void tarjan(int u)
{
	dfn[u] = low[u] = ++cnt; st[++top] = u;
	for(int i = 0; i < e[u].size(); i++)
	{
		int v = e[u][i];
		if(!dfn[v]) {tarjan(v); low[u] = min(low[u], low[v]);}
		else if(!d[v]) low[u] = min(low[u], dfn[v]);
	}
	if(low[u] == dfn[u])
	{
		sum++;
		while(st[top] != u) {if(a[st[top]]) val[sum] = min(val[sum], a[st[top]]); else t[sum] = st[top]; d[st[top--]] = sum;}
		if(a[st[top]])val[sum] = min(val[sum], a[st[top]]); else t[sum] = min(t[sum], st[top]); d[st[top--]] = sum;
	}
}
/*----------------------------------------函数*/
int main()
{
    n = read(); p = read();
    for(int i = 1; i <= p; i++) {int x = read(); a[x] = read();}
    r = read(); emm(val); emm(t);
    for(int i = 1; i <= r; i++)
    {
    	int x = read(), y = read();
    	e[x].push_back(y);
    }
    for(int i = 1; i <= n; i++) if(!dfn[i] && a[i]) tarjan(i);
    for(int i = 1; i <= n; i++) if(!dfn[i]) {printf("NO\n%d", i); return 0;}
    for(int u = 1; u <= n; u++)
    	for(int i = 0; i < e[u].size(); i++)
    		if(d[u] != d[e[u][i]]) in[d[e[u][i]]]++;
    for(int i = 1; i <= sum; i++)
	{
		if(in[i]) continue;
		ans += val[i];
	}
	printf("YES\n%d", ans);
	return 0;
}

6. 抢掠计划

/*
  Time: 2.1
  Worker: Blank_space
  Source: #10096. 「一本通 3.5 练习 4」抢掠计划
  强连通分量 缩点 建图 跑最长路 
  每一个强联通分量中可以取走所有点的值 
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#define ll long long
using namespace std; 
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int FFF = 0x8fffffff;
/*------------------------------------常量定义*/
struct edge {int v, w;};
vector <int> G1[B << 3];
vector <edge> G2[B << 3];
int n, m, s, p, a[B << 3], dfn[B << 3], low[B << 3], st[B << 3], top, cnt, sum, t[B << 3], _cnt, d[B << 3];
ll val[B << 3], ans, dis[B << 3];
bool vis[B << 3];
queue <int> q;
/*------------------------------------变量定义*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
/*----------------------------------------快读*/
void tarjan(int u)
{
	dfn[u] = low[u] = ++cnt; st[++top] = u;
	for(int i = 0; i < G1[u].size(); i++)
	{
		int v = G1[u][i];
		if(!dfn[v]) {tarjan(v); low[u] = min(low[u], low[v]);}
		else if(!d[v]) low[u] = min(low[u], dfn[v]);
	}
	if(low[u] == dfn[u])
	{
		sum++;
		while(st[top] != u) {d[st[top]] = sum; val[sum] += a[st[top--]];}
		d[st[top]] = sum; val[sum] += a[st[top--]];
	}
}
/*----------------------------------------函数*/
int main()
{
    n = read(); m = read();
    for(int i = 1; i <= m; i++)
    {
    	int x = read(), y = read();
    	G1[x].push_back(y);
    }
    for(int i = 1; i <= n; i++) a[i] = read();
    s = read(); p = read();
    for(int i = 1; i <= p; i++) t[++_cnt] = read();
    for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
    for(int u = 1; u <= n; u++)
    	for(int i = 0; i < G1[u].size(); i++)
    	{
    		int v = G1[u][i];
    		if(d[u] != d[v]) G2[d[u]].push_back((edge){d[v], val[d[v]]});
    	}
    dis[d[s]] = val[d[s]]; q.push(d[s]);
    while(!q.empty())
    {
    	int u = q.front(); q.pop(); vis[u] = 0;
    	for(int i = 0; i < G2[u].size(); i++)
    	{
    		int v = G2[u][i].v, w = G2[u][i].w;
    		if(dis[v] < dis[u] + w)
    		{
    			dis[v] = dis[u] + w;
    			if(!vis[v]) {q.push(v); vis[v] = 1;}
    		}
    	}
    }
    for(int i = 1; i <= _cnt; i++) ans = max(ans, dis[d[t[i]]]);
    printf("%lld", ans);
	return 0;
}


第七个单独拿出来了

详见:2-SAT问题

posted @ 2021-02-01 10:29  Blank_space  阅读(93)  评论(0)    收藏  举报
// // // // // // //