D7 割点 割边 强连通分量
今天几道是模板题;
第一道:(粘不了链接呜呜呜)
题目描述
n个城市之间有通讯网络,每个城市都有通讯交换机,直接或间接与其它城市连接。因电子设备容易损坏,需给通讯点配备备用交换机。
但备用 交换机数量有限,不能全部配备,只能给部分重要城市配置。
于是规定:如果某个城市由于交换机损坏,不仅本城市通讯中断,还造成其它城市通讯中断,则配备备 用交换机。
请你根据城市线路情况,计算需配备备用交换机的城市个数,及需配备备用交换机城市的编号。
友情提示:图论常见的坑点,重边,自环,还有对本题来说的不连通
输入格式
第一行,一个整数n,表示共有n个城市(2<=n<=20000)
下面有若干行(<=60000):每行2个数a、b,a、b是城市编号,表示a与b之间有直接通讯线路。
输出格式
第一行,1个整数m,表示需m个备用交换机。
下面有m行,每行有一个整数,表示需配备交换机的城市编号。
输出顺序按编号由小到大。如果没有城市需配备备用交换机则输出0。
题解:对于中心交换器a,b;只需以a为根节点进行遍历,寻找割点的子树中是否含有b节点;如果有,则是一种可行解;
#include<bits/stdc++.h> using namespace std; #define N 500001 struct gg { int y,next; }a[N]; inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') ch=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } int n,m,ans[N<<1],lin[N<<1],tot,cnt,dfn[N<<1],low[N<<1]; int root; inline void add(int x,int y) { a[++tot].y=y; a[tot].next=lin[x]; lin[x]=tot; } void tarjan(int x) { dfn[x]=low[x]=++cnt; int flag=0; for(int i=lin[x];i;i=a[i].next) { int y=a[i].y; if(!dfn[y]) { tarjan(y); low[x]=min(low[x],low[y]); if(low[y]>=dfn[x]) { flag++; if(x!=root||flag>1) ans[x]=1; } } else low[x]=min(low[x],dfn[y]); } } int main() { freopen("gd.in","r",stdin); freopen("gd.out","w",stdout); n=read(); tot=1; memset(dfn,0,sizeof(dfn)); memset(lin,0,sizeof(lin)); memset(low,0,sizeof(low)); memset(ans,0,sizeof(ans)); int x,y; while(scanf("%d%d",&x,&y)!=EOF) { if(x==y) continue; add(x,y);add(y,x); } for(int i=1;i<=n;i++) if(dfn[i]==0) root=i,tarjan(i); int k=0; for(int i=1;i<=n;i++) if(ans[i]) ++k; cout<<k<<endl; for(int i=1;i<=n;i++) if(ans[i]) cout<<i<<endl; return 0; }
第二题:LUOGU5058
摘自:https://www.luogu.org/blog/da32s1da/solution-p5058
首先非常明显的是,只能把服务器建在割点上,因为只有这样,才能捕获到全部的数据。
考虑如何求x,y两点之间的必经的割点。
很明显需要满足4个条件
考虑我们有一个u点,它连了一个v点,那么u点需要满足4个条件。
- u不是起点终点。因为题目要求在中间服务器上建嗅探器。
- u是割点。
- 终点(y)的dfn应该大于等于v点的dfn,因为要确保终点在v点或之后被访问到,即u点为必经的点。
- 终点(y)的low应该大于等于u点的dfn,因为要确保终点必须要经过u点。
剩下的没什么了,板子
注意我们从x点开始tarjan,就不用看x点是不是割点了。
#include<bits/stdc++.h> using namespace std; #define N 500001 inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } int n,m,tot,lin[N],dfn[N],low[N],k,cnt,root,x,y; int ans=0x7fffffff; struct gg { int y,next; }a[N<<1]; void add(int x,int y) { a[++tot].y=y; a[tot].next=lin[x]; lin[x]=tot; } void tarjan(int u) { dfn[u]=low[u]=++cnt; for(int i=lin[u];i;i=a[i].next) { int v=a[i].y; if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); if(u!=x&&u!=y) if(dfn[u]<=low[v]&&dfn[v]<=dfn[y]&&low[y]>=dfn[x]) ans=min(ans,u); } else low[u]=min(low[u],dfn[v]); } } int main() { n=read(); memset(dfn,0,sizeof(dfn)); memset(lin,0,sizeof(lin)); while(cin>>x>>y&&x!=0&&y!=0) add(x,y),add(y,x); x=read();y=read(); tarjan(x); if(ans!=0x7fffffff) cout<<ans<<endl; else cout<<"No solution"<<endl; return 0; }
第三题:LUOGU P3469
#include<bits/stdc++.h> using namespace std; #define N 500001 inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } int lin[N],dfn[N],low[N],tot,n,m,x,y,cnt,size[N],cut[N]; long long ans[N]; struct gg { int y,next; }a[N<<1]; void add(int x,int y) { a[++tot].y=y; a[tot].next=lin[x]; lin[x]=tot; } void tarjan(int x) { dfn[x]=low[x]=++cnt; size[x]=1; int flag=0,sum=0; for(int i=lin[x];i;i=a[i].next) { int y=a[i].y; if(!dfn[y]) { tarjan(y); size[x]+=size[y]; low[x]=min(low[x],low[y]); if(low[y]>=dfn[x]) { flag++; ans[x]+=(long long)size[y]*(n-size[y]); sum+=size[y]; if(x!=1||flag>1) cut[x]=1; } } else low[x]=min(low[x],dfn[y]); } if(cut[x]) ans[x]+=(long long)(n-sum-1)*(sum+1)+(n-1); else ans[x]=2*(n-1); } int main() { freopen("test.in","r",stdin); freopen("test.out","w",stdout); n=read();m=read(); tot=1; for(int i=1;i<=m;i++) { int x,y; x=read();y=read(); if(x==y) continue; add(x,y),add(y,x); } tarjan(1); for(int i=1;i<=n;i++) printf("%lld\n",ans[i]); return 0; }
第四题:
题目描述
天凯是苏联的***。苏联有n个城市,某些城市之间修筑了公路。任意两个城市都可以通过公路直接或者间接到达。 天凯发现有些公路被毁坏之后会造成某两个城市之间无法互相通过公路到达。这样的公路就被称为dangerous pavement。 为了防止美帝国对dangerous pavement进行轰炸,造成某些城市的地面运输中断,天凯决定在所有的dangerous pavement驻扎重兵。可是到底哪些是dangerous pavement呢?你的任务就是找出所有这样的公路。
输入格式
第一行n,m(1<=n<=100000, 1<=m<=300000),分别表示有n个城市,总共m条公路。
以下m行每行两个整数a, b,表示城市a和城市b之间修筑了直接的公路。
输出格式
输出有若干行。每行包含两个数字a,b(a < b),表示 < a,b >是dangerous pavement。请注意:输出时,所有的数对< a,b>必须按照a从小到大排序输出;如果a相同,则根据b从小到大排序。
寻找割边输出,最后输出处理一下,并且保证题目要求的顺序;
#include<bits/stdc++.h> using namespace std; #define N 500001 inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } struct gg { int y,next; }a[N<<1]; struct pink { int x,y; }b[N<<1]; bool mycmp(pink a,pink b) { if(a.x!=b.x) return a.x<b.x; else return a.y<b.y; } int lin[N*2],dfn[N*2],low[N*2],tot,cnt,n,m,x,y,bridge[N*2]; inline void add(int x,int y) { a[++tot].y=y; a[tot].next=lin[x]; lin[x]=tot; } inline void tarjan(int x,int in_edge) { dfn[x]=low[x]=++cnt; for(int i=lin[x];i;i=a[i].next) { int y=a[i].y; if(!dfn[y]) { tarjan(y,i); low[x]=min(low[x],low[y]); if(low[y]>dfn[x]) bridge[i]=bridge[i^1]=1; } else if(i!=(in_edge^1)) low[x]=min(low[x],dfn[y]); } } int main() { freopen("danger.in","r",stdin); freopen("danger.out","w",stdout); n=read();m=read(); memset(lin,0,sizeof(lin)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); tot=1; for(int i=1;i<=m;i++) { x=read();y=read(); add(x,y);add(y,x); } for(int i=1;i<=n;i++) if(dfn[i]==0) tarjan(i,0); int k=0; for(int i=2;i<=tot;i+=2) if(bridge[i]) { k++; if(a[i].y<a[i^1].y) b[k].x=a[i].y,b[k].y=a[i^1].y; else b[k].x=a[i^1].y,b[k].y=a[i].y; } sort(b+1,b+1+k,mycmp); for(int i=1;i<=k;i++) cout<<b[i].x<<' '<<b[i].y<<endl; return 0; }
第五题:luoguP1726
#include<bits/stdc++.h> #define N 100500 using namespace std; template<typename T>inline void read(T &x) { x=0; register int f=1; register char ch=getchar(); while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar(); x*=f; } struct gg { int next,y; }e[N<<1]; int lin[N<<1],dfn[N],low[N],cnt,tot,hl,n,m,ans[N]; int du[N],id[N],all[N]; bool v[N]; stack<int>s; inline void add(int x,int y) { cnt++; e[cnt].y=y; e[cnt].next=lin[x]; lin[x]=cnt; } void tarjan(int x) { dfn[x]=low[x]=++tot; s.push(x);v[x]=true; for(int i=lin[x];i;i=e[i].next) { int u=e[i].y; if(!dfn[u]) { tarjan(u); low[x]=min(low[x],low[u]); } else if(v[u]) low[x]=min(low[x],dfn[u]); } int k; if(low[x]==dfn[x]) { ++hl; do { k=s.top();s.pop(); v[k]=false; id[k]=hl;all[hl]++; }while(x!=k); } } int main() { freopen("classroom.in","r",stdin); freopen("classroom.out","w",stdout); read(n);read(m); int a,b,c; for(int i=1;i<=m;i++) { read(a);read(b);read(c);add(a,b); if(c==2) add(b,a); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); int t=0,dcc; for(int i=1;i<=hl;i++) { if(t<all[id[i]]) {dcc=i;t=all[id[i]];} } cout<<t<<endl; int k=0; for(int j=1;j<=n;j++) { if(id[j]==id[dcc]) ans[++k]=j; } cout<<ans[1]; for(int i=2;i<=k;i++) cout<<' '<<ans[i]; return 0; }