BZOJ 2229 / Luogu P3329 [ZJOI2011]最小割 (分治最小割板题)

题面

求所有点对的最小割中<=c的数量

分析

分治最小割板题

首先,注意这样一个事实:如果(X,Y)是某个s1-t1最小割,(Z,W)是某个s2-t2最小割,那么X∩Z、X∩W、Y∩Z、Y∩W这四项不可能均非空。也就是说,最小割不可能相互跨立。

这个蕴含了,最多一共有N-1个不同的s-t最小割。只需把这些割找出来即可。

寻找的方法:首先,在V中任意找两个点a,b,求最大流,把V划分为割X-Y,之后对X、Y分别递归地进行划分。这样就能得到N-1个割了。
(摘自hzwer的博客)

CODE

#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
template<typename T>inline void read(T &num) {
    char ch; int flg=1;
    while((ch=getc())<'0'||ch>'9')if(ch=='-')flg=-flg;
    for(num=0;ch>='0'&&ch<='9';num=num*10+ch-'0',ch=getc());
    num*=flg;
}
const int inf = 1e9;
const int MAXN = 155;
const int MAXM = 100005;
int n, m, Q, fir[MAXN], S, T, cnt;
struct edge { int to, nxt, c; }e[MAXM];
inline void add(int u, int v, int cc) { //双向边
    e[cnt] = (edge){ v, fir[u], cc }; fir[u] = cnt++;
    e[cnt] = (edge){ u, fir[v], cc }; fir[v] = cnt++;
}
int dis[MAXN], vis[MAXN], info[MAXN], cur, q[MAXN];
inline bool bfs() {
    int head = 0, tail = 0;
    vis[S] = ++cur; q[tail++] = S; dis[S] = 0;
    while(head < tail) {
        int u = q[head++];
        for(int i = fir[u]; ~i; i = e[i].nxt)
            if(e[i].c && vis[e[i].to] != cur)
                vis[e[i].to] = cur, dis[e[i].to] = dis[u] + 1, q[tail++] = e[i].to;
    }
    if(vis[T] == cur) memcpy(info, fir, (n+1)<<2);
    return vis[T] == cur;
}
int dfs(int u, int Max) {
    if(u == T || !Max) return Max;
    int flow=0, delta;
    for(int i = fir[u]; ~i; i = e[i].nxt)
        if(e[i].c && dis[e[i].to] == dis[u] + 1 && (delta=dfs(e[i].to, min(e[i].c, Max-flow)))) {
            e[i].c -= delta, e[i^1].c += delta, flow += delta;
            if(flow == Max) return flow;
        }
	if(!flow) dis[u] = -1;
    return flow;
}
inline int dinic() {
    int flow=0, x;
    while(bfs()) while((x=dfs(S, inf))) flow+=x;
    return flow;
}
inline void clear() {
	for(int i = 0; i < cnt; i+=2)
		e[i].c = e[i^1].c = (e[i].c + e[i^1].c) >> 1;
}
int now; bool mark[MAXN];
void cut(int u) {
	mark[u] = 1;
	for(int i = fir[u]; ~i; i = e[i].nxt)
		if(e[i].c && !mark[e[i].to]) cut(e[i].to);
}
int a[MAXN], tmp[MAXN], ans[MAXN][MAXN];
void solve(int l, int r) {
	if(l == r) return; clear();
	S = a[l], T = a[r]; int MinCut = dinic();
	memset(mark, 0, sizeof mark); cut(S);
	for(int i = 1; i <= n; ++i) if(mark[i])
		for(int j = 1; j <= n; ++j) if(!mark[j])
			ans[i][j] = ans[j][i] = min(ans[i][j], MinCut);
	int L = l, R = r;
	for(int i = l; i <= r; ++i)
		if(mark[a[i]]) tmp[L++] = a[i];
		else tmp[R--] = a[i];
	for(int i = l; i <= r; ++i) a[i] = tmp[i];
	solve(l, L-1), solve(R+1, r);
}
int arr[12000], top;
int main () {
	int kase, x;
	read(kase);
	while(kase--) {
		memset(fir, -1, sizeof fir); cnt = 0;
		memset(ans, 0x7f, sizeof ans);
		read(n), read(m);
		for(int i = 1, x, y, z; i <= m; ++i)
			read(x), read(y), read(z), add(x, y, z);
		for(int i = 1; i <= n; ++i) a[i] = i;
		solve(1, n);
		top = 0;
		for(int i = 1; i <= n; ++i)
			for(int j = i+1; j <= n; ++j)
				arr[top++] = ans[i][j];
		sort(arr, arr+top);
		read(Q);
		while(Q--)
			read(x), printf("%d\n", upper_bound(arr, arr+top, x)-arr);
		puts("");
	}
}
posted @ 2019-12-14 14:51  _Ark  阅读(92)  评论(0编辑  收藏  举报