- 传送门 -
http://acm.hdu.edu.cn/showproblem.php?pid=2433
Time Limit: 10000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3246 Accepted Submission(s): 1112
Problem Description
One day, Tom traveled to a country named BGM. BGM is a small country, but there are N (N <= 100) towns in it. Each town products one kind of food, the food will be transported to all the towns. In addition, the trucks will always take the shortest way. There are M (M <= 3000) two-way roads connecting the towns, and the length of the road is 1.
Let SUM be the total distance of the shortest paths between all pairs of the towns. Please write a program to calculate the new SUM after one of the M roads is destroyed.
Input
The input contains several test cases.
The first line contains two positive integers N, M. The following M lines each contains two integers u, v, meaning there is a two-way road between town u and v. The roads are numbered from 1 to M according to the order of the input.
The input will be terminated by EOF.
Output
Output M lines, the i-th line is the new SUM after the i-th road is destroyed. If the towns are not connected after the i-th road is destroyed, please output “INF” in the i-th line.
Sample Input
5 4
5 1
1 3
3 2
5 4
2 2
1 2
1 2
Sample Output
INF
INF
INF
INF
2
2
Source
2008 Asia Chengdu Regional Contest Online
- 题意 -
给出 n 个点, m 条边. (边权都为1)
定义SUM[i] = i 到其它节点的最短距离之和.
ans = SUM[1] + SUM[2] + ... + SUM[n].
求在摧毁第x条边的情况下ans最小值.(1 <= x <= m)
- 思路 -
BFS.
因为边权为1, 所以我们可以直接(从某点开始)搜索得到到达其它点的最短路.
先对每个点处理出一棵树(表示其它节点到它的最短距离), 并记录每个点的这棵树包含了哪些边.
处理每一条边时, 我们都要遍历每一棵树, 考虑这条边不在树上或者这条边有重边(断掉这条还有同样的边存在)时, 可以直接得到答案, 否则就要把边断掉, 重新BFS得到答案.
细节见代码.
PS :
需要注意的几点:
1. 拆边后要把边重新连起来.
2. 预处理出每棵树表示的每个点到某点的距离和, 在拆边处理时不会更改, 以便下次处理.
3. 蜜汁卡常...
- 代码 -
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 100 + 5;
const int M = 3e3 + 5;
int DIS[N], VIS[N], SUM[N];
int MAP[N][N], X[M], Y[M];
int USED[N][N][N];
int n, m, ans;
queue<int>q;
void init() {
memset(MAP, 0, sizeof (MAP));
memset(SUM, 0, sizeof (SUM));
memset(USED, 0, sizeof (USED));
ans = 0;
}
int BFS(int x, int flag) {
while (!q.empty())
q.pop();
memset(VIS, 0, sizeof (VIS));
memset(DIS, 0, sizeof (DIS));
q.push(x);
VIS[x] = 1;
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = 1; i <= n; ++i)
if (!VIS[i] && MAP[i][u]) { //就是这里!!!写反了导致我卡了巨久!!!为这种卡常姿势献上膝盖!!!
VIS[i] = 1;
if (flag) { USED[x][i][u] = 1; USED[x][u][i] = 1; } //表示找 x 到其它点的最短路中包括了 i - u 这条路
DIS[i] += DIS[u] + 1; //边权为 1
q.push(i);
}
}
int tot = 0;
for (int i = 1; i <= n; ++i) {
if (DIS[i] == 0 && i != x)
return -1;
tot += DIS[i];
}
return tot;
}
int main() {
while (~scanf("%d%d", &n, &m)) {
init();
for (int i = 1; i <= m; ++i) {
int x, y;
scanf("%d%d", &x, &y);
X[i] = x; Y[i] = y;
MAP[x][y] ++;
MAP[y][x] ++;
}
for (int i = 1; i <= n; ++i) {
SUM[i] = BFS(i, 1);
if (SUM[i] == -1) {
ans = -1;
break;
}
ans += SUM[i];
}
for (int i = 1; i <= m; ++i) {
MAP[X[i]][Y[i]] --;
MAP[Y[i]][X[i]] --;
int tot, tmp = ans;
if (tmp == -1)
printf("INF
"); //图原本就不连通
else if (MAP[X[i]][Y[i]] > 0)
printf("%d
", ans); //重边
else {
for (int j = 1; j <= n; ++j)
if (USED[j][X[i]][Y[i]]) {
tot = BFS(j, 0);
if (tot == -1) {
printf("INF
");
break;
}
tmp += tot - SUM[j]; //边在树中则算拆边后的情况
} //边不在树中则不作处理
if (tot != -1) printf("%d
", tmp);
}
MAP[X[i]][Y[i]] ++;
MAP[Y[i]][X[i]] ++; //把边连回来
}
}
return 0;
}