• 周四. 4月 25th, 2024

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

#边双缩点,并查集#洛谷 2416 泡芙

admin

11月 28, 2021

题目

给出一张 (n) 个点,(m) 条边的无向图,边权为0或1,
问是否存在一条每条边最多经过一次的路径中至少有一条边的边权为1,
多组数据给出起点和终点


分析

考虑在一个边双中只要有边权为1的边,那么这个边双就是合法的,
那么跑一遍Tarjan之后,给边双缩点,
不存在当且仅当起点和终点所在边双不合法且路径上(桥)边权全为0,
那么将桥边权为0的用并查集缩成连通块,如果连通那么边权全为0


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=300011; struct node{int y,w,next;}e[N<<1];
int dfn[N],low[N],f[N],bridge[N<<1],et=1,n,w[N],tot,as[N],col[N],cnt;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline signed min(int x,int y){return x<y?x:y;}
inline signed getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
inline void tarjan(int x,int F){
	dfn[x]=low[x]=++tot;
	for (rr int i=as[x];i;i=e[i].next)
	if (!dfn[e[i].y]){
		tarjan(e[i].y,i^1),low[x]=min(low[x],low[e[i].y]);
		if (dfn[x]<low[e[i].y]) bridge[i]=bridge[i^1]=1;
	}else if (i!=F) low[x]=min(low[x],dfn[e[i].y]);
}
inline void dfs(int x){
	col[x]=cnt;
	for (rr int i=as[x];i;i=e[i].next)
	    if (!col[e[i].y]&&!bridge[i]) dfs(e[i].y);
}
inline void Merge(int x,int y){
	rr int fa=getf(x),fb=getf(y);
	if (fa==fb) return; f[fa]=fb;
} 
signed main(){
	n=iut();
	for (rr int m=iut();m;--m){
		rr int x=iut(),y=iut(),w=iut();
		e[++et]=(node){y,w,as[x]},as[x]=et;
		e[++et]=(node){x,w,as[y]},as[y]=et;
	}
    tarjan(1,0);
    for (rr int i=1;i<=n;++i)
	    if (!col[i]) ++cnt,dfs(i);
	for (rr int i=1;i<=cnt;++i) f[i]=i;
    for (rr int i=2;i<=et;i+=2)
	if (col[e[i^1].y]==col[e[i].y]) w[col[e[i].y]]+=e[i].w;
	    else if (!e[i].w) Merge(col[e[i^1].y],col[e[i].y]);
	for (rr int Q=iut();Q;--Q){
		rr int x=col[iut()],y=col[iut()];
		if (w[x]||w[y]) puts("YES");
		else if (getf(x)==getf(y)) puts("NO");
		    else puts("YES");
	}
	return 0;
}

《#边双缩点,并查集#洛谷 2416 泡芙》有一个想法

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注