我花了 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;
}