題面如圖
分析
直接做的複雜度是 ,問題在於每割一條邊對每個點都要做一次最短路,而其實某些點的最短路並沒有發生變化。
於是我們有一個優化的思路:
如果這條邊不在某些點爲出發點的最短路上,顯然不用從這個點跑一次最短路;如果在的話就跑一次最短路
如何實現這個操作呢?
我們先可以先處理出每一個點出發的最短路路徑(由於邊長是 ,於是用 即可),這其實是一顆以 爲頂點的生成樹(如果最短路有多種方案,只記一種)。用 表示 這條邊是否在 爲頂點的生成樹上。每次枚舉邊的時候判斷一下,如果在以 爲根的生成樹上,就跑一次最短路。
思考一下複雜度?
有 棵樹, 一棵樹只有 條邊,也就是說在樹上的邊數爲
,所以最多跑 次最短路,每次最短路是 的,所以總複雜度是
代碼如下
#include <bits/stdc++.h>
#define N 105
#define M 3005
#define inf 2147483647
using namespace std;
struct node{
int a, b, c, n;
bool operator < (const node & A) const{
return c > A.c;
}
}d[M * 2];
int h[N], cnt, dis[N], s[N], t[N], n, m, v[M * 2], ret[N][N], u[N][N][N], flag;
void cr(int a, int b, int c){
d[++cnt].a = a; d[cnt].b = b; d[cnt].c = c; d[cnt].n = h[a]; h[a] = cnt;
}
void bfs(int x, int p){
int i, a, b;
if(!p) memset(u[x], 0, sizeof(u[x]));
s[x] = 0;
queue<int> q;
q.push(x);
memset(dis, -1, sizeof(dis));
dis[x] = 0;
while(!q.empty()){
a = q.front(); q.pop();
for(i = h[a]; i; i = d[i].n){
if(v[i]) continue;
b = d[i].b;
if(dis[b] > -1) continue;
dis[b] = dis[a] + 1;
if(!p) u[x][a][b] = u[x][b][a] = 1;
q.push(b);
}
}
for(i = 1; i <= n; i++){
if(dis[i] == -1) flag = 1;
s[x] += dis[i];
}
}
int main(){
int i, j, k, a, b, sum;
while(scanf("%d%d", &n, &m) != EOF){
memset(h, 0, sizeof(h));
memset(ret, 0, sizeof(ret));
cnt = flag = sum = 0;
for(i = 1; i <= m; i++){
scanf("%d%d", &a, &b);
cr(a, b, 1);
cr(b, a, 1);
ret[a][b]++;
ret[b][a]++;
}
for(i = 1; i <= n; i++) bfs(i, 0), t[i] = s[i];
if(flag){
for(i = 1; i <= m; i++) printf("INF\n");
continue;
}
for(i = 1; i <= cnt; i += 2){
flag = sum = 0;
a = d[i].a; b = d[i].b;
v[i] = v[i + 1] = 1;
for(j = 1; j <= n; j++){
if(!u[j][a][b] || ret[a][b] > 1) sum += t[j];
else{
//printf("%d %d %d===\n", j, a, b);
bfs(j, 1);
sum += s[j];
if(flag) break;
}
}
if(flag) printf("INF\n");
else printf("%d\n", sum);
v[i] = v[i + 1] = 0;
}
}
return 0;
}