【模板整合计划】图论—图的连通性
【模板整合计划】图论—图的连通性
一:【欧拉路问题】
1.【欧拉回路】
(1).【逐步插入回路法 (Hierholzer)】
#include<algorithm>
#include<cstring>
#include<cstdio>
#define Re register int
const int N=1e5+3,M=2e5+3;
int n,m,x,y,m_,op;
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
struct Euler_Circle__Undirected_Graph{//无向图欧拉回路
int o,du[N],cur[N],ans[M],pan[M<<1],head[N];
struct QAQ{int to,next;}a[M<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void dfs(Re x){
for(Re i=head[x];i;i=head[x]){
head[x]=a[i].next;//当前弧优化
if(!pan[i]){
pan[i]=pan[i^1]=1;
dfs(a[i].to);
ans[++ans[0]]=(i&1)?-i/2:i/2;//正数表示 x->y,负数表示y->x
}
}
}
inline void sakura(){
in(n),in(m),m_=m,o=1;
while(m_--)in(x),in(y),add(x,y),add(y,x),++du[x],++du[y];
for(Re i=1;i<=n;cur[i]=head[i],++i)if(du[i]&1){puts("NO");return;}//无向图点度数必须为偶数
for(Re i=1;i<=n;++i)if(du[i]){dfs(i);break;}//任意找一个有度数的起点
if(ans[0]!=m){puts("NO");return;}//跑不满m条边
puts("YES");
for(Re i=ans[0];i>=1;--i)printf("%d ",ans[i]);
}
}T1;
struct Euler_Circle__Directed_Graph{//有向图欧拉回路
int o,ru[N],chu[N],cur[N],ans[M],head[N];
struct QAQ{int to,next;}a[M<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void dfs(Re x){
for(Re i=head[x];i;i=head[x]){
head[x]=a[i].next;//当前弧优化
dfs(a[i].to);
ans[++ans[0]]=i;
}
}
inline void sakura(){
in(n),in(m),m_=m;
while(m_--)in(x),in(y),add(x,y),++ru[y],++chu[x];
for(Re i=1;i<=n;cur[i]=head[i],++i)if(ru[i]!=chu[i]){puts("NO");return;}//有向图点入度必须等于出度
for(Re i=1;i<=n;++i)if(chu[i]){dfs(i);break;}//任意找一个有出度的起点
if(ans[0]!=m){puts("NO");return;}//跑不满m条边
puts("YES");
for(Re i=ans[0];i>=1;--i)printf("%d ",ans[i]);
}
}T2;
int main(){
in(op);
if(op==1)T1.sakura();//无向图
if(op==2)T2.sakura();//有向图
}
2.【欧拉路】
二:【无向图】
1.【割】
(1).【割点】
#include<algorithm>
#include<cstring>
#include<cstdio>
#define Re register int
const int N=2e4+3,M=2e5+3;
int n,m,x,y,ans,cut[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
struct Cut_Point{
int o,dfn_o,low[N],dfn[N],head[N];
struct QAQ{int to,next;}a[M];
inline int min(Re a,Re b){return a<b?a:b;}
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void tarjan(Re x,Re root){
Re son=0;dfn[x]=low[x]=++dfn_o;
for(Re i=head[x],to;i;i=a[i].next)
if(!dfn[to=a[i].to]){
tarjan(to,root),++son;
low[x]=min(low[x],low[to]);
cut[x]|=(x!=root&&low[to]>=dfn[x]);
}
else low[x]=min(low[x],dfn[to]);
cut[x]|=(x==root&&son>1),ans+=cut[x];
}
inline void cut_point(){for(Re i=1;i<=n;++i)if(!dfn[i])tarjan(i,i);}
}T1;
int main(){
in(n),in(m);
while(m--)in(x),in(y),T1.add(x,y),T1.add(y,x);
T1.cut_point();
printf("%d\n",ans);
for(Re i=1;i<=n;++i)if(cut[i])printf("%d ",i);
}
(2).【割边(桥)】
【模板】旅游航道 \(\text{[Loj10102]}\)
#include<algorithm>
#include<cstring>
#include<cstdio>
#define Re register int
using namespace std;
const int N=3e4+3,M=3e4+3;
int n,m,x,y,ans;
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
struct Tarjan{
int o,dfn_o,dfn[N],low[N],head[N],bridge[M<<1];
struct QAQ{int to,next;}a[M<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void CL(){
memset(bridge,0,sizeof(bridge));
memset(head,0,sizeof(head));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
dfn_o=ans=0,o=1;
}
inline void tarjan(Re x,Re p){
dfn[x]=low[x]=++dfn_o;
for(Re i=head[x],to;i;i=a[i].next)
if(!dfn[to=a[i].to]){
tarjan(to,i);
low[x]=min(low[x],low[to]);
if(low[to]>dfn[x])bridge[i]=bridge[i^1]=1,++ans;
}
else if(i!=(p^1))low[x]=min(low[x],dfn[to]);
}
inline void get_bridg(){for(Re i=1;i<=n;++i)if(!dfn[i])tarjan(i,-1);}
}T1;
int main(){
while(scanf("%d%d",&n,&m)&&n&&m){
T1.CL();
while(m--)in(x),in(y),T1.add(x,y),T1.add(y,x);
T1.get_bridg();
printf("%d\n",ans);
}
}
2.【双连通分量】
(1).【边双连通分量 (e-DCC)】
【模板】冗余路径 \(\text{Redundant Paths}\) \(\text{[P2860]}\)
#include<algorithm>
#include<cstdio>
#define Re register int
using namespace std;
const int N=5003,M=10003;
int n,m,x,y,Q_o,leaf,ip[N],du[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
struct Tarjan{
int o,dfn_o,dfn[N],low[N],head[N],bridge[M<<1];
struct QAQ{int to,next;}a[M<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void tarjan(Re x,Re p){
dfn[x]=low[x]=++dfn_o;
for(Re i=head[x],to;i;i=a[i].next)
if(!dfn[to=a[i].to]){
tarjan(to,i);
low[x]=min(low[x],low[to]);
if(low[to]>dfn[x])bridge[i]=bridge[i^1]=1;
}
else if(i!=(p^1))low[x]=min(low[x],dfn[to]);
}
inline void dfs(Re x){
ip[x]=Q_o;
for(Re i=head[x];i;i=a[i].next)
if(!ip[a[i].to]&&!bridge[i])dfs(a[i].to);
}
inline void SuoPoint(){
for(Re i=1;i<=n;++i)if(!dfn[i])tarjan(i,-1);
for(Re i=1;i<=n;++i)if(!ip[i])++Q_o,dfs(i);
}
}T1;
int main(){
in(n),in(m),T1.o=1;
while(m--)in(x),in(y),T1.add(x,y),T1.add(y,x);
T1.SuoPoint();
for(Re i=2;i<=T1.o;i+=2)
if((x=ip[T1.a[i].to])!=(y=ip[T1.a[i^1].to]))++du[x],++du[y];
for(Re i=1;i<=Q_o;++i)leaf+=(du[i]==1);
printf("%d",leaf+1>>1);
}
(2).【点双连通分量 (v-DCC)】
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#define LL long long
#define Re register int
using namespace std;
const int N=1003,M=503;
int n,m,x,y,T,Q_o,tmp,ans1,cut[N];LL ans2;
std::vector<int>dcc[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline int max(Re a,Re b){return a>b?a:b;}
struct Tarjan{
int o,t,dfn_o,Q[N],dfn[N],low[N],head[N];
struct QAQ{int to,next;}a[M<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void CL(){
memset(head,0,sizeof(head));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(cut,0,sizeof(cut));
dfn_o=t=o=0;
}
inline void tarjan(Re x,Re rt){
Re son=0;Q[++t]=x,dfn[x]=low[x]=++dfn_o;
for(Re i=head[x],to;i;i=a[i].next)
if(!dfn[to=a[i].to]){
tarjan(to,rt),++son;
low[x]=min(low[x],low[to]);
if(low[to]>=dfn[x]){
if(x!=rt)cut[x]=1;
else cut[x]|=son>1;
++Q_o;
while(1){
dcc[Q_o].push_back(Q[t]);
if(to==Q[t--])break;
}
dcc[Q_o].push_back(x);
}
}
else low[x]=min(low[x],dfn[to]);
}
inline void SuoPoint(){for(Re i=1;i<=n;++i)if(!dfn[i])tarjan(i,i);}
}T1;
int main(){
while(scanf("%d",&m)&&m){
for(Re i=1;i<=Q_o;++i)dcc[i].clear();
T1.CL(),Q_o=ans1=n=0,ans2=1;
while(m--)in(x),in(y),T1.add(x,y),T1.add(y,x),n=max(n,max(x,y));
T1.SuoPoint();
for(Re i=1;i<=Q_o;++i){
tmp=0,n=dcc[i].size();
for(Re j=0;j<n;++j)tmp+=cut[dcc[i][j]];
if(!tmp)ans1+=2,ans2*=(n-1)*n/2;
else if(tmp<2)ans1+=1,ans2*=(n-1);
}
printf("Case %d: %d %lld\n",++T,ans1,ans2);
}
}
三:【有向图】
1.【强连通分量 (SCC)】
(1).【Tarjan】
#include<cstring>
#include<cstdio>
#include<queue>
#define Re register int
const int N=1e4+3,M=1e5+3;
int n,m,x,y,Q_o,ans,A[N],ru[N],gs[N],ip[N],dp[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline int min(Re a,Re b){return a<b?a:b;}
inline int max(Re a,Re b){return a>b?a:b;}
struct Tarjan{
int o,t,dfn_o,Q[N],low[N],dfn[N],pan[N],head[N];
struct QAQ{int x,to,next;}a[M];
inline void add(Re x,Re y){a[++o].x=x,a[o].to=y,a[o].next=head[x],head[x]=o;}
inline void tarjan(Re x){
dfn[x]=low[x]=++dfn_o,Q[++t]=x,pan[x]=1;
for(Re i=head[x],to;i;i=a[i].next)
if(!dfn[to=a[i].to])tarjan(to),low[x]=min(low[x],low[to]);
else if(pan[to])low[x]=min(low[x],dfn[to]);
if(low[x]==dfn[x]){
++Q_o;
while(1){
ip[Q[t]]=Q_o,gs[Q_o]+=A[Q[t]],pan[Q[t]]=0;
if(x==Q[t--])break;
}
}
}
inline void SuoPoint(){for(Re i=1;i<=n;++i)if(!dfn[i])tarjan(i);}
}T1;
struct Tuopu{
int o,ru[N],pan[N],head[N];std::queue<int>Q;
struct QAQ{int to,next;}a[M<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void creat(){
for(Re i=1;i<=T1.o;++i)
if((x=ip[T1.a[i].x])!=(y=ip[T1.a[i].to]))
++ru[y],add(x,y);
}
inline void tuopu(){
for(Re i=1;i<=Q_o;++i)if(!ru[i])dp[i]=gs[i],Q.push(i);
while(!Q.empty()){
Re x=Q.front();Q.pop();
for(Re i=head[x],to;i;i=a[i].next){
to=a[i].to;
dp[to]=max(dp[to],dp[x]+gs[to]);
if(!(--ru[to]))Q.push(to);
}
}
}
}T2;
int main(){
in(n),in(m);
for(Re i=1;i<=n;++i)in(A[i]);
while(m--)in(x),in(y),T1.add(x,y);
T1.SuoPoint(),T2.creat(),T2.tuopu();
for(Re i=1;i<=Q_o;++i)ans=max(ans,dp[i]);
printf("%d",ans);
}
(2).【Kosaraju】
#include<cstring>
#include<cstdio>
#include<queue>
#define Re register int
const int N=1e6+3,M=1e6+3;
int n,m,Q_o,ans,X[M],Y[M],A[N],ip[N],gs[N],dp[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline int max(Re a,Re b){return a>b?a:b;}
struct Kosaraju{
int o,ndfn_o,nQ[N],pan[N],head[N];
struct QAQ{int x,to,next;}a[M<<1];
inline void add(Re x,Re y){a[++o].x=x,a[o].to=y,a[o].next=head[x],head[x]=o;}
inline void dfs1(Re x){
pan[x]=1;
for(Re i=head[x];i;i=a[i].next)if(!pan[a[i].to])dfs1(a[i].to);
nQ[++ndfn_o]=x;
}
inline void dfs2(Re x){
ip[x]=Q_o,gs[Q_o]+=A[x];
for(Re i=head[x];i;i=a[i].next)if(!ip[a[i].to])dfs2(a[i].to);
}
inline void kosaraju(){
for(Re i=1;i<=m;++i)add(X[i],Y[i]);
for(Re i=1;i<=n;++i)if(!pan[i])dfs1(i);//跑逆dfs序
memset(head,0,sizeof(head));
memset(pan,0,sizeof(pan));
memset(a,0,sizeof(a));o=0;
for(Re i=1;i<=m;++i)add(Y[i],X[i]);//建反图
for(Re i=ndfn_o;i;--i)if(!ip[nQ[i]])++Q_o,dfs2(nQ[i]);//缩点
}
}T1;
struct Tuopu{
int o,x,y,ru[N],pan[N],head[N];std::queue<int>Q;
struct QAQ{int to,next;}a[M<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void creat(){
for(Re i=1;i<=T1.o;++i)
if((x=ip[T1.a[i].x])!=(y=ip[T1.a[i].to]))
++ru[y],add(x,y);
}
inline void tuopu(){
creat();
for(Re i=1;i<=Q_o;++i)if(!ru[i])dp[i]=gs[i],Q.push(i);
while(!Q.empty()){
Re x=Q.front();Q.pop();
for(Re i=head[x],to;i;i=a[i].next){
to=a[i].to;
dp[to]=max(dp[to],dp[x]+gs[to]);
if(!(--ru[to]))Q.push(to);
}
}
}
}T2;
int main(){
in(n),in(m);
for(Re i=1;i<=n;++i)in(A[i]);
for(Re i=1;i<=m;++i)in(X[i]),in(Y[i]);
T1.kosaraju(),T2.tuopu();
for(Re i=1;i<=Q_o;++i)ans=max(ans,dp[i]);
printf("%d",ans);
}
2.【2-SAT】
【模板】\(\text{2-SAT}\) 问题 \(\text{[P4782]}\)
(1).【Tarjan】
#include<algorithm>
#include<cstring>
#include<cstdio>
#define Re register int
const int N=2e6+3,M=2e6+3;
int n,m,p1,p2,x1,x2,Q_o,ip[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
struct Tarjan{
int o,t,dfn_o,Q[N],low[N],dfn[N],pan[N],head[N];
struct QAQ{int to,next;}a[M];
inline int min(Re a,Re b){return a<b?a:b;}
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void tarjan(Re x){
low[x]=dfn[x]=++dfn_o,Q[++t]=x,pan[x]=1;
for(Re i=head[x],to;i;i=a[i].next)
if(!dfn[to=a[i].to])tarjan(to),low[x]=min(low[x],low[to]);
else if(pan[to])low[x]=min(low[x],dfn[to]);
if(low[x]==dfn[x]){
++Q_o;
while(1){
ip[Q[t]]=Q_o,pan[Q[t]]=0;
if(x==Q[t--])break;
}
}
}
inline void SuoPoint(){for(Re i=1;i<=2*n;++i)if(!dfn[i])tarjan(i);}
}T1;
int main(){
in(n),in(m);
while(m--)in(p1),in(x1),in(p2),in(x2),T1.add(p1+(x1^1)*n,p2+x2*n),T1.add(p2+(x2^1)*n,p1+x1*n);
T1.SuoPoint();
for(Re i=1;i<=n;++i)if(ip[i]==ip[i+n])return !printf("IMPOSSIBLE");
puts("POSSIBLE");
for(Re i=1;i<=n;++i)printf("%d ",ip[i]>ip[i+n]);
}
(2).【Kosaraju】
#include<algorithm>
#include<cstring>
#include<cstdio>
#define Re register int
const int N=2e6+3,M=2e6+3;
int n,m,mm,p1,p2,x1,x2,Q_o,X[M],Y[M],ip[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
struct Kosaraju{
int o,ndfn_o,nQ[N],pan[N],head[N];
struct QAQ{int x,to,next;}a[M];
inline void add(Re x,Re y){a[++o].x=x,a[o].to=y,a[o].next=head[x],head[x]=o;}
inline void dfs1(Re x){
pan[x]=1;
for(Re i=head[x];i;i=a[i].next)if(!pan[a[i].to])dfs1(a[i].to);
nQ[++ndfn_o]=x;
}
inline void dfs2(Re x){
ip[x]=Q_o;
for(Re i=head[x];i;i=a[i].next)if(!ip[a[i].to])dfs2(a[i].to);
}
inline void kosaraju(){
for(Re i=1;i<=m;++i)add(X[i],Y[i]);
for(Re i=1;i<=2*n;++i)if(!pan[i])dfs1(i);//跑逆dfs序
memset(head,0,sizeof(head));
memset(pan,0,sizeof(pan));
memset(a,0,sizeof(a));o=0;
for(Re i=1;i<=m;++i)add(Y[i],X[i]);//建反图
for(Re i=ndfn_o;i;--i)if(!ip[nQ[i]])++Q_o,dfs2(nQ[i]);//缩点
}
}T1;
int main(){
in(n),in(mm);
while(mm--)in(p1),in(x1),in(p2),in(x2),X[++m]=p1+(x1^1)*n,Y[m]=p2+x2*n,X[++m]=p2+(x2^1)*n,Y[m]=p1+x1*n;
T1.kosaraju();
for(Re i=1;i<=n;++i)if(ip[i]==ip[i+n])return !printf("IMPOSSIBLE");
puts("POSSIBLE");
for(Re i=1;i<=n;++i)printf("%d ",ip[i]<ip[i+n]);
}