目录
题目
- 1000ms
- 131072K
一场无硝烟的战争即将爆发,蒜头君和花椰妹接到上级任务——破坏敌方通信网络。破坏敌方通信网络并不是一件简单任务,蒜头君和花椰妹只能各自破坏敌方通信网络中的一个节点。破坏两个节点后,如果敌方至少有两个节点无法通信(除了已被破坏的两个节点),就认为蒜头君和花椰妹破坏成功。为了简化问题,请你计算蒜头君和花椰妹有多少种方法可以成功破坏敌方通信网络。
输入格式
第一行输入两个整数 n(3≤n≤1000) 和 m(0≤m≤10000) ,表示敌方有 m 条双向电缆连接 n 个节点(节点编号从 1 到 n)。
接来下 m 行,每行有两个整数 a ,b(1≤a,b≤n) 表示节点 a 和节点 b 通过一条双向电缆连接。
输出格式
输出一个整数,表示蒜头君和花椰妹有多少种方法可以成功破坏敌方通信网络。
样例输入1
4 4 1 2 2 3 3 4 4 1样例输出1
2样例输入2
7 9 1 2 1 3 2 3 3 4 3 5 4 5 5 6 5 7 6 7样例输出2
11
题解:
知识点:tarjan算法、割点、连通块
分析:
我们可以枚举每一个点,并假设这个点被蒜头君删除
当这个点被删除,若剩下的图被分成了≥3个连通块,那么只要删去了这个点,剩下n−1个点无论删去哪个都是有效答案,对答案的贡献为n-1
若剩下的图被分成了两个连通块:
- 若有两个块都只有一个点,那么取哪一个点都不能使得该图不连通,因为拿哪一个点都会使该图还有一个点,故对答案的贡献为0
- 若有一个块只有一个点,另一个块有≥2个点,则删去第一个块的唯一一个点后,原来两个块又变成了一个块,所以不能够删那个点。易证,删除另一个块的任意一个点都满足要求。此时这种情况对答案的贡献是n-2
- 否则, 两个块都有≥2个点,任意取一个点都可行,贡献n-1
若剩下的图只有一个连通块,那么就只能取其割点了。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#include<algorithm>
#include<stack>
#define _for(i,a,b) for (int i=(a);i<=(b);i++)
using namespace std;
const int N=1e3+5,M=2e4+5;
struct node{
int u,v;
node(){}
node(int _u,int _v){
u=_u;
v=_v;
}
};
int e[M],ne[M],h[N],idx;//链式前向星
int del;//当前被删除的点
int times=0;//时间戳
int dfn[N],low[N];//时间戳、tarjan
int cc[N];//当前状态下第i个连通块的数量
int cut_cnt=0,cc_cnt=0;
bool iscut[N],vis[N];
void init(){
memset(h,-1,sizeof(h));
idx=0;
}
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void tarjan(int u,int fa){
dfn[u]=low[u]=++times;
int child=0;
for (int i=h[u];~i;i=ne[i]){
int v=e[i];
if (v==del){
continue;
}
if (dfn[v]==0){
child++;
tarjan(v,u);
low[u]=min(low[u],low[v]);
if (dfn[u]<=low[v] && !iscut[u]){
iscut[u]=true;
cut_cnt++;
}
}else if (dfn[v]<dfn[u] && v!=fa){
low[u]=min(low[u],dfn[v]);
}
}
if (fa<=0 && child==1){
iscut[u]=false;
cut_cnt--;
}
}
void dfs(int u){//连通块
vis[u]=true;
cc[cc_cnt]++;
for (int i=h[u];~i;i=ne[i]){
int v=e[i];
if (v==del || vis[v]){
continue;
}
dfs(v);
}
}
int main(){
init();
int n,m;
scanf("%d%d",&n,&m);
_for(i,1,m){
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
int ans=0;
_for(i,1,n){
cut_cnt=cc_cnt=times=0;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(iscut,0,sizeof(iscut));
memset(cc,0,sizeof(cc));
memset(vis,0,sizeof(vis));
del=i;
if (i+1>n){
tarjan(1,-1);
}else{
tarjan(i+1,-1);
}
_for(j,1,n){
if (!vis[j] && j!=del){
cc_cnt++;
dfs(j);
}
}
if (cc_cnt>=3){
ans+=n-1;
}else if (cc_cnt==2){
if (cc[1]==1 && cc[2]>1 || cc[1]>1 && cc[2]==1){
ans+=n-2;
}else if (cc[1]>1 && cc[2]>1){
ans+=n-1;
}
}else{
ans+=cut_cnt;
}
}
printf("%d\n",ans>>1);
return 0;
}