洛谷-【图论2-4】连通性问题2

P2863 [USACO06JAN] The Cow Prom S

题目描述

有一个 n 个点,m 条边的有向图,请求出这个图点数大于 1 的强连通分量个数。

输入格式

第一行为两个整数 n 和 m。

第二行至 m+1 行,每一行有两个整数 a 和 b,表示有一条从 a 到 b 的有向边。

输出格式

仅一行,表示点数大于 1 的强连通分量个数。

输入输出样例

输入 #1复制

复制代码
5 4
2 4
3 5
1 2
4 1

输出 #1复制

复制代码
1

说明/提示

数据规模与约定

对于全部的测试点,保证 2≤n≤104,2≤m≤5×104,1≤a,b≤n。

实现代码:

cpp 复制代码
#include<bits/stdc++.h>
#define maxn 10001
using namespace std;
vector<int>G[maxn];
stack<int>s;
int n,m;
int dfn[maxn],used[maxn],vis[maxn],low[maxn],color[maxn],num[maxn],colornum=0,cnt=0,ans=0;
void paint(int x)
{
    s.pop();
    color[x]=colornum;
    num[colornum]++;
    vis[x]=false;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++cnt;
    s.push(x);
    vis[x]=used[x]=true;
    for(int i=0;i<G[x].size();i++)
    {
        int q=G[x][i];
        if (!dfn[q])
        {
            tarjan(q);
            low[x]=min(low[x],low[q]);
        }
        else if (vis[q]) low[x]=min(low[x],dfn[q]);
    }
    if (low[x]==dfn[x])
    {
        colornum++;
        while(s.top()!=x)
        {
            int t=s.top();
            paint(t);
        }
        paint(x);
    }
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int u,v;
        cin>>u>>v;
        G[u].push_back(v);
    }
    for(int i=1;i<=n;i++)
    {
        if (!used[i]) tarjan(i);
    }
    for(int i=1;i<=colornum;i++)
    {
        if (num[i]>1) ans++;
    }
    cout<<ans;
    return 0;
}

P3387 【模板】缩点

题目描述

给定一个 n 个点 m 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

输入格式

第一行两个正整数 n,m。

第二行 n 个整数,其中第 i 个数 ai​ 表示点 i 的点权。

第三至 m+2 行,每行两个整数 u,v,表示一条 u→v 的有向边。

输出格式

共一行,最大的点权之和。

输入输出样例

输入 #1复制

复制代码
2 2
1 1
1 2
2 1

输出 #1复制

复制代码
2

说明/提示

对于 100% 的数据,1≤n≤104,1≤m≤105,0≤ai​≤103。

实现代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

const int maxn=10000+15;
int n,m,sum,tim,top,s;
int p[maxn],head[maxn],sd[maxn],dfn[maxn],low[maxn];
int stac[maxn],vis[maxn];
int h[maxn],in[maxn],dist[maxn];
struct EDGE
{
	int to;int next;int from;
}edge[maxn*10],ed[maxn*10];
void add(int x,int y)
{
	edge[++sum].next=head[x];
	edge[sum].from=x;
	edge[sum].to=y;
	head[x]=sum;
}
void tarjan(int x)
{
	low[x]=dfn[x]=++tim;
	stac[++top]=x;vis[x]=1;
	for (int i=head[x];i;i=edge[i].next)
	{
		int v=edge[i].to;
		if (!dfn[v]) {
		tarjan(v);
		low[x]=min(low[x],low[v]);
	}
	    else if (vis[v])
	    {
	    	low[x]=min(low[x],low[v]);
		}
	}
	if (dfn[x]==low[x])
	{
		int y;
		while (y=stac[top--])
		{
			sd[y]=x;
			vis[y]=0;
			if (x==y) break;
			p[x]+=p[y];
		}
	}
}
int topo()
{
	queue <int> q;
	int tot=0;
	for (int i=1;i<=n;i++)
	if (sd[i]==i&&!in[i])
	{
		q.push(i);
        dist[i]=p[i];
	 } 
	while (!q.empty())
	{
		int k=q.front();q.pop();
		for (int i=h[k];i;i=ed[i].next)
		{
			int v=ed[i].to;
			dist[v]=max(dist[v],dist[k]+p[v]);
			in[v]--;
			if (in[v]==0) q.push(v);
		}
	}
    int ans=0;
    for (int i=1;i<=n;i++)
    ans=max(ans,dist[i]);
    return ans;
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	scanf("%d",&p[i]);
	for (int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);
	}
	for (int i=1;i<=n;i++)
	if (!dfn[i]) tarjan(i);
	for (int i=1;i<=m;i++)
	{
		int x=sd[edge[i].from],y=sd[edge[i].to];
		if (x!=y)
		{
			ed[++s].next=h[x];
			ed[s].to=y;
			ed[s].from=x;
			h[x]=s;
			in[y]++;
		}
	}
	printf("%d",topo());
	return 0;
}

P4782 【模板】2-SAT

题目描述

有 n 个布尔变量 x1​∼xn​,另有 m 个需要满足的条件,每个条件的形式都是 「xi​ 为 true / false 或 xj​ 为 true / false」。比如 「x1​ 为真或 x3​ 为假」、「x7​ 为假或 x2​ 为假」。

2-SAT 问题的目标是给每个变量赋值使得所有条件得到满足。

输入格式

第一行两个整数 n 和 m,意义如题面所述。

接下来 m 行每行 4 个整数 i, a, j, b,表示 「xi​ 为 a 或 xj​ 为 b」(a,b∈{0,1})

输出格式

如无解,输出 IMPOSSIBLE

否则输出 POSSIBLE,下一行 n 个整数 x1​∼xn​(xi​∈{0,1}),表示构造出的解。

输入输出样例

输入 #1复制

复制代码
3 1
1 1 3 0

输出 #1复制

复制代码
POSSIBLE
0 0 0

说明/提示

1≤n,m≤106 , 前 3 个点卡小错误,后面 5 个点卡效率。

由于数据随机生成,可能会含有 10 0 10 0 之类的坑,但按照最常规写法的写的标程没有出错,各个数据点卡什么的提示在标程里。

实现代码:

cpp 复制代码
#include <cstdio>
#include <algorithm>
using std::min;
namespace IO_number{
	int read(){
		int x =0; char c =getchar(); bool f =0;
		while(c < '0' || c > '9') (c == '-') ? f =1, c =getchar() : c =getchar();
		while(c >= '0' && c <= '9') x =(x<<1)+(x<<3)+(48^c), c =getchar();
		return (f) ? -x : x;
	}
	
	void write(const int &x){
		if(x/10)
			write(x/10);
		putchar('0'+x%10);
	}
}
using namespace IO_number;
const int MAXN =1e6+20;

int first[MAXN<<1], tote;
struct edge{
	int to, nxt;
}e[MAXN<<1];

inline void addedge(const int &u, const int &v){
	++tote, e[tote].to =v, e[tote].nxt =first[u], first[u] =tote;
}

int scc_id[MAXN<<1], Scc_id;

int dfn[MAXN<<1], low[MAXN<<1], Dfn;
int stk[MAXN<<1], stk_top;
bool instk[MAXN<<1];

void tarjan(const int &u){
	low[u] =dfn[u] =++Dfn;
	stk[stk_top++] =u;
	instk[u] =1;
	for(int l =first[u]; l; l =e[l].nxt){
		if(!dfn[e[l].to]){
			tarjan(e[l].to);
			low[u] =min(low[u], low[e[l].to]);
		}
		else if(instk[e[l].to])
			low[u] =min(low[u], dfn[e[l].to]);
	}
	if(low[u] == dfn[u]){
		++Scc_id;
		while(stk[stk_top] != u){
			scc_id[stk[stk_top-1]] =Scc_id;
			instk[stk[stk_top-1]] =0;
			--stk_top;
		}
	}
}
int main(){
	int n =read(), m =read();
	for(int t =0; t < m; ++t){
		int i =read(), a =read(), j =read(), b =read();
		/* [1, n] -> false, [n+1, 2n] -> true */
		// x_i, x_j 均不满足不能同时成立 //
		addedge(i+(a^1)*n, j+b*n);
		addedge(j+(b^1)*n, i+a*n);
	}
	
	for(int i =1; i <= 2*n; ++i){
		if(!dfn[i])
			tarjan(i);
		if(i <= n && scc_id[i] == scc_id[i+n])
			return puts("IMPOSSIBLE") && 0;
	}
	puts("POSSIBLE");
	for(int i =1; i <= n; ++i)
		write((scc_id[i+n] < scc_id[i])), putchar(' ');
}

P3469 [POI 2008] BLO-Blockade

题目描述

B 城有 n 个城镇(从 1 到 n 标号)和 m 条双向道路。

每条道路连结两个不同的城镇,没有重复的道路,所有城镇连通。

把城镇看作节点,把道路看作边,容易发现,整个城市构成了一个无向图。

请你对于每个节点 i 求出,把与节点 i 关联的所有边去掉以后(不去掉节点 i 本身),无向图有多少个有序点对 (x,y),满足 x 和 y 不连通。

注:这里的"有序点对"可以理解为 (x,y) 和 (y,x) 不相同 (x=y)。

输入格式

第一行包含两个整数 n 和 m。

接下来 m 行,每行包含两个整数 a 和 b,表示城镇 a 和 b 之间存在一条道路。

输出格式

输出共 n 行,每行输出一个整数。

第 i 行输出的整数表示把与节点 i 关联的所有边去掉以后(不去掉节点 i 本身),无向图有多少个有序点对 (x,y),满足 x 和 y 不连通。

输入输出样例

输入 #1复制

复制代码
5 5
1 2
2 3
1 3
3 4
4 5

输出 #1复制

复制代码
8
8
16
14
8

说明/提示

n≤105,m≤5×105。

实现代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000010;
inline int read()
{
	int x=0,t=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')t=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x*t;
}
int n,m,head[maxn],num=0;
int dfn[maxn],low[maxn],size[maxn],tot=0;
long long ans[maxn];
bool cut[maxn];
struct node{
	int v,nex;
}e[maxn];
void add(int u,int v)
{
	e[++num].v=v;
	e[num].nex=head[u];
	head[u]=num;
}
void tarjan(int u)
{
	dfn[u]=low[u]=++tot;
	size[u]=1;
	int flag=0,sum=0;
	for(int i=head[u];i;i=e[i].nex)
	{
		int v=e[i].v;
		if(!dfn[v])
		{
			tarjan(v);
			size[u]+=size[v];
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u])
			{
				ans[u]+=(long long)size[v]*(n-size[v]);
				sum+=size[v];
				flag++;
				if(u!=1||flag>1) cut[u]=true;
			}
		}
		else low[u]=min(low[u],dfn[v]);
	}
	if(!cut[u]) ans[u]=2*(n-1);
	else ans[u]+=(long long)(n-sum-1)*(sum+1)+(n-1);
}
int main()
{
	n=read(),m=read();
	for(int i=1;i<=m;i++)
	{
		int x,y;
		x=read(),y=read();
		add(x,y),add(y,x);
	}
	tarjan(1);
	for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);
	return 0;
}
相关推荐
扫地的小何尚1 小时前
掌握 Agentic AI 技术:AI Agent 定制方法全景与实践路径
大数据·人工智能·算法·ai·llm·agent·nvidia
Brilliantwxx1 小时前
【C++】 二叉搜索树
开发语言·c++·算法
为何创造硅基生物10 小时前
C语言 结构体内存对齐规则(通俗易懂版)
c语言·开发语言
吃好睡好便好10 小时前
在Matlab中绘制横直方图
开发语言·学习·算法·matlab
星寂樱易李10 小时前
iperf3 + Python-- 网络带宽、网速、网络稳定性
开发语言·网络·python
仰泳之鹅10 小时前
【C语言】自定义数据类型2——联合体与枚举
c语言·开发语言·算法
之歆10 小时前
DAY_12JavaScript DOM 完全指南(二):实战与性能篇
开发语言·前端·javascript·ecmascript
于小猿Sup11 小时前
VMware在Ubuntu22.04驱动Livox Mid360s
linux·c++·嵌入式硬件·自动驾驶
cen__y11 小时前
Linux12(Git01)
linux·运维·服务器·c语言·开发语言·git