P4214 [CERC2015]Juice Junctions 题解
最小割树板子题。
一开始我用通常我写的 \(\text{Isap}\) 去试了试,只有 \(\text{65pts}\)。
后面尝试使用 \(\text{Dinic}\),居然就有了 \(\text{100pts}\),跑了 \(14s\)。
最后题解区说可以用 \(\text{EK}\) 跑,我试了一下,居然只跑了 \(\text{6s}\)。
好吧,\(\text{EK}\) 的崛起了属于是。
可为啥 \(\text{Isap}\) 比 \(\text{Dinic}\) 慢呢,令人极度疑惑。
思路
应该没有人不会最小割树吧。
我觉得能找到这题的应该都会最小割树。
所以这里我可以对这道题网络流做法的迷惑的复杂度,做一些感性理解。
由于 \(\text{EK}\) 是单次不断寻找可行边,然后我们即可发现,本题中每个点的度数小于等于三,即到汇点的可行边只有三条,而又限制了每条边的流量严格为一。
故其网络流的最大复杂度应该为 \(O(3n)\) ,就相当于 \(O(n)\) 了。
故总复杂度应为 \(O(n^2)\)
而 \(\text{dinic}\) 需要不断的使用 \(\text{dfs}\) 寻找,自然多了很多其他的情况,所以更劣一些。
给 \(\text{EK}\) 的代码吧:
#include <bits/stdc++.h>
using namespace std;
const int N = 3010;
const int M = 20000;
const int inf = 1e9 + 7;
int T , n , m , s , t , q , cnt , maxflow;
int id[N] , que[N] , vis[N] , tmp[N] , head[N];
struct edge
{
int to , nxt , val , sum;
}e[M];
struct Pre
{
int v , edge;
}pre[N];
inline int read()
{
int asd = 0 , qwe = 1; char zxc;
while(!isdigit(zxc = getchar())) if(zxc == '-') qwe = -1;
while(isdigit(zxc)) asd = asd * 10 + zxc - '0' , zxc = getchar();
return asd * qwe;
}
inline void add(int x , int y , int z)
{
e[++cnt] = {y , head[x] , z , z} , head[x] = cnt;
e[++cnt] = {x , head[y] , 0 , 0} , head[y] = cnt;
}
inline int bfs()
{
queue<int> q;
memset(que , 0 , sizeof que);
memset(pre , -1 , sizeof pre);
q.push(s) , que[s] = 1;
while(q.empty() == 0)
{
int x = q.front(); q.pop();
for(int i = head[x];i;i = e[i].nxt)
if(que[e[i].to] == 0 && e[i].val)
{
que[e[i].to] = 1;
pre[e[i].to] = (Pre){x , i};
if(e[i].to == t) return 1;
q.push(e[i].to);
}
}
return 0;
}
inline void init()
{
for(int i = 0;i <= cnt;i++) e[i].val = e[i].sum;
}
inline int EK(int ss , int ts)
{
s = ss , t = ts , maxflow = 0 , init();
while(bfs())
{
int minn = inf;
for(int i = t;i != s;i = pre[i].v) minn = min(minn , e[pre[i].edge].val);
for(int i = t;i != s;i = pre[i].v) e[pre[i].edge].val -= minn;
maxflow += minn;
}
return maxflow;
}
struct GHT
{
edge tr[M];
int tot , hed[N] , ans[N][N];
inline void add_tree(int x , int y , int z)
{
tr[++tot] = {y , hed[x] , z} , hed[x] = tot;
tr[++tot] = {x , hed[y] , z} , hed[y] = tot;
}
inline void find(int now)
{
vis[now] = 1;
for(int i = head[now];i;i = e[i].nxt)
if(vis[e[i].to] == 0 && e[i].val) find(e[i].to);
}
inline void build(int l ,int r)
{
if(l >= r) return;
int x = id[l] , y = id[l + 1] , sum = EK(x , y);
int ls = l , rs = r;
memset(vis , 0 , sizeof vis) , find(x);
add_tree(x , y , sum);
for(int i = l;i <= r;i++)
tmp[(vis[id[i]] ? ls++ : rs--)] = id[i];
for(int i = l;i <= r;i++) id[i] = tmp[i];
build(l , ls - 1) , build(rs + 1 , r);
}
inline void get(int now , int fa , int dis , int gen)
{
ans[gen][now] = dis;
for(int i = hed[now];i;i = tr[i].nxt)
if(tr[i].to != fa) get(tr[i].to , now , min(dis , tr[i].val) , gen);
}
inline void solve()
{
for(int i = 1;i <= n;i++) id[i] = i;
build(1 , n);
for(int i = 1;i <= n;i++) get(i , 0 , inf , i);
}
inline int ask()
{
int res = 0;
for(int i = 1;i <= n;i++)
for(int j = i + 1;j <= n;j++) res += ans[i][j];
return res;
}
}tree;
int main()
{
n = read() , m = read() , cnt = 1;
for(int i = 1;i <= m;i++)
{
int x = read() , y = read();
add(x , y , 1) , add(y , x , 1);
}
tree.solve();
cout << tree.ask();
return 0;
}