洛谷 3469 & bzoj 1123 [POI2008]BLO-Blockade 题解

跟我一起念:poi!poi!poi!

题意简述

1e5个点2e5条边的无向联通图,对于每个点i,输出:删除i之后有多少有序对(x,y)使得x到y不连通,1<=x,y<=n,x,y不一定不等于i。(此题应援bgm:Maxi poi☆poi poi poi!)

思路框架

求割点的时候,顺便求出$DFS$树。然后我们知道,一个数组$a$中任意有序选两个不相同数的积的和,即$\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[i!=j]ij$,就等于$(\sum a_i)^2-\sum(a_i^2)$(即:和平方-平方和)。

树上对于点$u$,如果不是割点,令序列$a$=${size[son1],size[son2]…size[sonk],n-size[u],1}$。(即:删掉点$u$后,被划分出的所有部分(包括$u$自成一家)的点的数量的集合)。然后求$a$的平方和-和平方即珂。由于这些东西的和显然是$n$,求出平方和即珂。

简单维护一下。如果发现$u$不是割点,直接把答案变成$2(n-1)$(因为$x,y$都珂以$=u$。)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

#include <bits/stdc++.h>using namespace std;
namespace Flandre_Scarlet
{
#define N 155555
#define int long long
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)

class Graph
{
public:
int head[N];
int EdgeCount;
struct Edge
{
int To,Label,Next;
}Ed[2666666];
void clear(int _V=N,int _E=2666666)
{
memset(Ed,-1,sizeof(Edge)*(_E));
memset(head,-1,sizeof(int)*(_V));
EdgeCount=-1;
}
void AddEdge(int u,int v,int w=1)
{
Ed[++EdgeCount]=(Edge){v,w,head[u]};
head[u]=EdgeCount;
}
void Add2(int u,int v,int w=1) {AddEdge(u,v,w);AddEdge(v,u,w);}
int Start(int u) {return head[u];}
int To(int u){return Ed[u].To;}
int Label(int u){return Ed[u].Label;}
int Next(int u){return Ed[u].Next;}
}G;
void R1(int &x)
{
x=0;char c=getchar();int f=1;
while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=(f==1)?x:-x;
}
int n,m;
void Input()
{
R1(n),R1(m);
G.clear();
F(i,1,m)
{
int u,v;R1(u),R1(v);
G.Add2(u,v);
}
}

int DFSid[N],low[N],Time=0;bool Cut[N];
//Cut[i]: i是否是割点
int size[N];int part[N];int s2[N];
//size[i]:子树上有多少点(包括自己)
//part[i]: 被分成了多少块,珂以理解为上面说的a序列的长度
//s2[i]: 删掉点i之后a序列的平方和
void Tarjan(int u,int f) //求割点,顺便得到DFS树
{
DFSid[u]=low[u]=++Time;
size[u]=s2[u]=1;
int tmp=0;
Tra(i,u)
{int v=__v;
if (!DFSid[v])
{
Tarjan(v,u);
size[u]+=size[v];
low[u]=min(low[u],low[v]);
if (low[v]>=DFSid[u])
{
tmp+=size[v];
Cut[u]=1;
++part[u];s2[u]+=size[v]*size[v]; //维护平方和
}
}
else if (v!=f)
{
low[u]=min(low[u],low[v]);
}
}
//tmp:所有儿子的size值之和
//n-tmp-1珂以理解为n-size[u]
if (Cut[u] and n-tmp-1!=0) ++part[u],s2[u]+=(n-tmp-1)*(n-tmp-1);
//u以上的部分
}
void Soviet()
{
Tarjan(1,1);
F(i,1,n)
{
if (Cut[i])
{
printf("%lld\n",n*n-s2[i]); //答案为n^2-s2[i]
}
else
{
printf("%lld\n",2*(n-1));
}
}
}

#define Flan void
Flan IsMyWife()
{
Input();
Soviet();
}
#undef int //long long
}
int main(){
Flandre_Scarlet::IsMyWife();
getchar();getchar();
return 0;
}
w