我花了 45 m i n 45min 45min A A A掉了 P O I \red{POI} POI的题!
难度: N O I / N O I + / C T S C ( b u s h i ) {\color{blue}{NOI/NOI+/CTSC}(bushi)} NOI/NOI+/CTSC(bushi)
毕竟也是人家国赛的题,刚拿道题觉得套板子就可以了,没想到细节有 亿 点 点 \color{Red}{亿点点} 亿点点
题意:
给你一个无向图 G G G,已知 G G G是联通的,求当把每个点封锁时,途中有多少点对 ( u , v ) (u,v) (u,v)不能相互抵达。
第一个坑点来了:细读题目,发现 ( u , v ) (u,v) (u,v)是有序的!也即 ( u , v ) (u,v) (u,v)和 ( v , u ) (v,u) (v,u)不算同一个点对!
画个图手玩一下可以发现,我们应该对封锁的结点 S S S进行讨论。分两种情况:
1. S is a cut-vertex. 1. \operatorname{S\space is\space a\space cut-vertex.} 1.S is a cut-vertex.
2. S i s n ′ t a c u t - v e r t e x . 2.\operatorname{S\space isn't\space a\space cut-vertex.} 2.S isn′t a cut-vertex.
我们不妨设图 G G G节点数为 ∣ G ∣ |G| ∣G∣,对于一个点 S S S,问题的答案为 f ( s ) f(s) f(s).若 S S S为一个割点,我们设它将图分成了 k k k个连通块,分别为 S i S_i Si, i ∈ [ 1 , k ] i\in[1,k] i∈[1,k].则答案为:
f ( S ) { 2 × ( ∣ G ∣ − 1 ) ; S i s n ′ t a c u t - v e r t e x . 2 × ( ∣ G ∣ − 1 ) + ∑ i = 1 k ∑ j = i k ∣ S i ∣ × ∣ S j ∣ ; S is a cut-vertex. f(S)\begin{cases} 2\times(|G|-1); \operatorname{S\space isn't\space a\space cut-vertex.}\\ 2\times(|G|-1)+\sum\limits_{i=1}^{k}\sum\limits_{j=i}^{k}|S_i|\times |S_j|; \operatorname {S\space is\space a\space cut-vertex.} \end{cases} f(S)⎩⎪⎨⎪⎧2×(∣G∣−1);S isn′t a cut-vertex.2×(∣G∣−1)+i=1∑kj=i∑k∣Si∣×∣Sj∣;S is a cut-vertex.
第一种情况可以 O ( 1 ) O(1) O(1)地算出来,第二种情况 ∣ S i ∣ |S_i| ∣Si∣的值可以在 t a r j a n tarjan tarjan的时候顺便算出来。
记 得 开 L o n g L o n g ! 记得开{\color{Blue}{Long\space Long}}! 记得开Long Long!
R e C o d e : {\color{SpringGreen}{\purple{Re}\space Code:}} Re Code:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5;
typedef long long LL;
int n,m,root,v[maxn],tag[maxn],Size[maxn];
int id,dfn[maxn],low[maxn];
bool cut[maxn];
LL ans[maxn];
vector<int> ver[maxn];
inline void read()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int u,v;
cin>>u>>v;
ver[u].push_back(v);
ver[v].push_back(u);
}
}
inline void tarjan(int x)
{
low[x]=dfn[x]=++id;
Size[x]=1;
int flag=0,sum=0;
for(int y=0;y<ver[x].size();y++)
{
int t=ver[x][y];
if(!dfn[t])
{
tarjan(t);
Size[x]+=Size[t];
low[x]=min(low[x],low[t]);
if(low[t]>=dfn[x])
{
flag++;
ans[x]+=(LL)Size[t]*(n-Size[t]);
sum+=Size[t];
if(x!=1||flag>1) cut[x]=true;
}
}
else
{
low[x]=min(low[x],dfn[t]);
}
}
if(cut[x])
ans[x]+=(LL)(n-sum-1)*(sum+1)+(n-1);
else
ans[x]=2*(n-1);
}
int main()
{
read();
tarjan(1);
for(int i=1;i<=n;i++)
{
cout<<ans[i]<<endl;
}
return 0;
}
A C C o d e : \green{AC\space Code:} AC Code:
#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;
}