bzoj2521
最小生成树+最小割
。。。我太zz了,没看出来全体减1是一个加1,看见后就是sb题了。。。
我们发现根据克鲁斯卡尔的过程,我们把边从小到大加入,如果两点已经相连就跳过,那么我们把所有小于等于这条边的边加入,如果能够联通这两点,那么说明得增加一些边的权值,所以我们求出和这条边的差+1,连边,使得没有一条路径连接两点且所有边权小于等于这条边的边权
#include<bits/stdc++.h> using namespace std; const int N = 1010, inf = 1 << 29; struct edge { int nxt, to, f; } e[N * 50]; struct Edge { int to, w; Edge(int to = 0, int w = 0) : to(to), w(w) {} }; vector<Edge> G[N]; int n, m, k, source, sink, W, cnt = 1, id; int d[N], head[N], iter[N], q[N * 100], used[N]; inline void link(int u, int v, int f) { e[++cnt].nxt = head[u]; head[u] = cnt; e[cnt].to = v; e[cnt].f = f; } inline void insert(int u, int v, int f) { link(u, v, f); link(v, u, 0); } bool bfs() { int l = 1, r = 0; memset(d, 0, sizeof(d)); d[source] = 1; q[++r] = source; while(l <= r) { int u = q[l++]; for(int i = head[u]; i; i = e[i].nxt) if(e[i].f && !d[e[i].to]) { d[e[i].to] = d[u] + 1; q[++r] = e[i].to; } } return d[sink] != 0; } int dfs(int u, int delta) { if(u == sink) return delta; int ret = 0; for(int &i = iter[u]; i && delta; i = e[i].nxt) if(e[i].f && d[e[i].to] == d[u] + 1) { int x = dfs(e[i].to, min(delta, e[i].f)); e[i].f -= x; e[i ^ 1].f += x; ret += x; delta -= x; } return ret; } inline int dinic() { int ret = 0; while(bfs()) { for(int i = 0; i <= n; ++i) iter[i] = head[i]; ret += dfs(source, inf); } return ret; }