Description
小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: “对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分是关于s,t的割。 对于带权图来说,将所有顶点处在不同部分的边的权值相加所得到的值定义为这个割的容量,而s,t的最小割指的是在关于s,t的割中容量最小的割” 现给定一张无向图,小白有若干个形如“图中有多少对点它们的最小割的容量不超过x呢”的疑问,小蓝虽然很想回答这些问题,但小蓝最近忙着挖木块,于是作为仍然是小蓝的好友,你又有任务了。
Input
输入文件第一行有且只有一个正整数T,表示测试数据的组数。 对于每组测试数据, 第一行包含两个整数n,m,表示图的点数和边数。 下面m行,每行3个正整数u,v,c(1<=u,v<=n,0<=c<=10^6),表示有一条权为c的无向边(u,v) 接下来一行,包含一个整数q,表示询问的个数 下面q行,每行一个整数x,其含义同题目描述。
Output
对于每组测试数据,输出应包括q行,第i行表示第i个问题的答案。对于点对(p,q)和(q,p),只统计一次(见样例)。
两组测试数据之间用空行隔开。
直接暴力显然超时,容易想到分治解决(然而并不会搞)。
首先需要知道最小割的一个性质,若不考虑多个最小割的情况,设S1-T1的最小割割集为C1,S2-T2的最小割割集为C2,则C1,C2必然不会相互跨立,这个结论可通过反证法得到。由这个性质我们可以发现整个图的最小割构成了一棵最小割树,即其最小割数量不超过n - 1个。
有了这个性质就很好分治了,我们每次从点集中任选S,T做最小割,将点集中的点按最后是与S或是T联通分为两个点集递归处理,并更新两边的点之间的最小割。
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#define For(i,j,k) for(int i = j;i <= (int)k;i++)
#define Forr(i,j,k) for(int i = j;i >= (int)k;i--)
#define Set(i,j) memset(i, j, sizeof i)
using namespace std;
const int N = 160, M = 6100;
struct Dinic{
int Begin[N], Next[M], to[M], cap[M], flow[M];
int cur[N], d[N], n, e, S, T;
void init(int _n){
Set(Begin, 0);
e = 1, n = _n;
}
void clearflow(){
Set(flow, 0);
}
void Add(int x, int y, int w){
cap[++e] = w, to[e] = y;
Next[e] = Begin[x], Begin[x] = e;
}
bool BFS(){
queue<int> q;
Set(d, 0);
q.push(S), d[S] = 1;
while(!q.empty()){
int s = q.front(); q.pop();
for(int i = Begin[s];i;i = Next[i]){
int u = to[i];
if(!d[u] && flow[i] < cap[i]) q.push(u), d[u] = d[s] + 1;
}
}
return d[T];
}
int DFS(int h, int Maxf){
if(h == T || !Maxf) return Maxf;
int sumf = 0;
for(int &i = cur[h];i;i = Next[i]){
int u = to[i], t;
if(d[u] == d[h] + 1 && (t = DFS(u, min(Maxf, cap[i] - flow[i])))){
flow[i] += t, flow[i ^ 1] -= t;
Maxf -= t, sumf += t;
}
if(!Maxf) return sumf;
}
return sumf;
}
int Mincut(int s, int t){
S = s, T = t;
int ret = 0;
while(BFS()){
For(i,1,n) cur[i] = Begin[i];
ret += DFS(s, 2e9);
}
return ret;
}
}F;
int T, n, m, q;
int cut[N][N], id[N], tmp[N];
void Solve(int L, int R){
if(L == R) return;
F.clearflow();
int ret = F.Mincut(id[L], id[R]), l = L, r = R;
For(i,1,n) if(F.d[i]) For(j,1,n) if(!F.d[j]) cut[i][j] = cut[j][i] = min(cut[i][j], ret);
For(i,L,R) tmp[F.d[id[i]] ? l++ : r--] = id[i];
For(i,L,R) id[i] = tmp[i];
Solve(L, r), Solve(l, R);
}
int main(){
scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &m);
F.init(n);
For(i,1,n) id[i] = i;
For(i,1,m){
int x, y, w;
scanf("%d%d%d", &x, &y, &w);
F.Add(x, y, w), F.Add(y, x, w);
}
Set(cut, 0x7f);
Solve(1, n);
scanf("%d", &q);
while(q--){
int p, Ans = 0;
scanf("%d", &p);
For(i,1,n) For(j,i+1,n) if(cut[i][j] <= p) ++Ans;
printf("%d\n", Ans);
}
if(T) puts("");
}
return 0;
}