10.27练习题 [POI2008]Blockade
KONO题面哒!
问题描述
某地区有n个城镇,一些城镇之间由无向边连接。每两个城镇之间至多只有一条直接连接的道路。人们可以从任意一个城镇直接或间接到达另一个城镇。每个城镇都有一个镇长。该地区正在进行镇长互访活动,每个镇长都要拜访其他所有镇长一次。
所以,计划会安排有一共n(n-1)次访问活动。 不幸的是,一个程序员总罢工正在进行中。作为抗议行动,程序员们计划封锁一些城镇,阻止人们进入,离开或者路过那里。他们正在讨论选择哪些城镇会导致最严重的后果。
编写一个程序:计算每个城镇,如果它被封锁,有多少访问活动不会发生,输出结果。
输入格式
第一行读入n,m,分别是城镇数目和道路数目
城镇编号1~n
接下来m行每行两个数字a,b,表示a和b之间有有一条无向边。
输出格式*
输出n行,每行一个数字,为第i个城镇被锁时不能发生的访问活动的数量。
样例输入
5 5
1 2
2 3
1 3
3 4
4 5
样例输出
8
8
16
14
8
数据范围
1≤n≤100000, 1≤m≤500000
(割点:即删掉此点原连通图边不连通的点)
把整个地区看成一个联通图,那么封锁一个城镇就相当于把这个点从连通图中删掉。那么,对于被删掉的这个点有两种不同的讨论方式:
(设被删掉的点为x)
- x不为割点:则删掉x后原连通图依然连通,所以只有与x号点的访问无法进行。此时答案为2*(n-1)。
- x为割点:则删掉x后原连通图将分裂成几个小块,则这几个小块与小块外的点将无法互相访问。所以此时的答案为
$$\sum^{k}_{i=1}{size[i]*(n-size[i])}$$
(size[i]表示第i个连通块大小)
注意:求完 x 的子树中所有割点后,还要加上剩下来的大联通块(n?sum?1)×(sum+1)和自己 1×(n?1)次访问。
(由于题面里并没有讲到的整个图联通,所以直接Tarjan(1)就可以了)
KONO代码哒!
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define int long long
using namespace std;
const int maxn=5000000;
struct edge {
int to,next;
} g[2*maxn+5];
int head[maxn],cnt;
int n,m;
int dfn[maxn],low[maxn],size[maxn],Dfn=0;
int ans[maxn];
void add(int from,int to) {
g[++cnt].next=head[from];
g[cnt].to=to;
head[from]=cnt;
}
void T(int x) {
dfn[x]=low[x]=++Dfn;
int i,j,sum=0;
size[x]=1;
for(i=head[x]; i; i=g[i].next) {
int v=g[i].to;
if(!dfn[v]) {
T(v);
size[x]+=size[v];
low[x]=min(low[x],low[v]);
if(dfn[x]<=low[v]) {
ans[x]+=sum*size[v];
sum+=size[v];
}
} else low[x]=min(low[x],dfn[v]);
}
ans[x]+=(n-sum-1)*sum;
ans[x]+=(n-1);
}
main() {
ios::sync_with_stdio(0);
cin>>n>>m;
int i,j;
for(i=1; i<=m; i++) {
int x,y;
cin>>x>>y;
add(x,y);
add(y,x);
}
T(1);
for(i=1; i<=n; i++) {
printf("%lld\n",2LL*ans[i]);
}
return 0;
}
(代码写出来奇短……)
原文地址:https://www.cnblogs.com/cooper233/p/11754212.html
时间: 2024-11-09 10:18:29