P3469 [POI2008]BLO-Blockade
挺难的。。
这道题以点与点的结合作为问题,问我们如果一个点被删掉,那么有几组点的有序结合不能进行。
题目给的是无向图,所以内存双倍。
不知道为什么,与割点有关系。
如果一个点不是割点,那么去掉这一个点,其他点之间的相互到达显然都不会被影响,而只有被割的点与其他点之间的才受影响。
如果一个点是割点,那么事情就大了。被割掉的每一边中的点互相无法到达。
对于第一种情况,答案显然是\(2(n -1)\)。
第二种就难了。
听一听这种思路:
如何判断一个点是割点?就是dfn[u] <= low[v]
。
那么如果跑tarjan的时候满足这个条件,那么是不是以v为根的dfs子树都会被割掉?
可以记录一个变量z,表示当前已经割掉的点的数目。
那么当我们新割了一些点,被割的点与新割的点就无法访问,我们就乘上去。同时更新z变量。
同病相怜的考虑完,还有一种情况:被割掉的点与不会被割掉的点之间无法相互访问。也乘上去。
显然那个\(2(n-1)\)是无论如何都会有的,我们加上去。
有时间再重新理解一下:
#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn = 100005, maxm = 500005;
struct Edges
{
int next, to;
} e[maxm << 1];
int head[maxn], tot;
int dfn[maxn], low[maxn], dtot;
long long ans[maxn];
int size[maxn];
int base;
int n, m;
bool cut[maxn];
int read()
{
int ans = 0, s = 1;
char ch = getchar();
while(ch > '9' || ch < '0')
{
if(ch == '-') s = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
ans = ans * 10 + ch - '0';
ch = getchar();
}
return s * ans;
}
void link(int u, int v)
{
e[++tot] = (Edges){head[u], v};
head[u] = tot;
}
void tarjan(int u, int root)
{
dfn[u] = low[u] = ++dtot;
size[u] = 1;
int child = 0;
long long z = 0;//已经被割掉的点的数量
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if(!dfn[v])
{
tarjan(v, root);
size[u] += size[v];
low[u] = std::min(low[u], low[v]);
if(dfn[u] <= low[v])//被割掉的子树
{
ans[u] += z * size[v];//已经被割的跟现在被割的无法相互到达
z += size[v];//update
}
}
low[u] = std::min(low[u], dfn[v]);
}
ans[u] += z * (n - z - 1);//割掉的点与未割掉的点无法相互到达
}
int main()
{
n = read(), m = read();
while(m--)
{
int u = read(), v = read();
link(u, v); link(v, u);
}
for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i, i);
for(int i = 1; i <= n; i++)
{
printf("%lld\n", (ans[i] + n - 1) << 1);
}
return 0;
}