CF343E Pumping Stations

\(n\) 个点 \(m\) 条边的带权无向图。

你需要构造一个排列,收益为 \(\sum_{i=2}^n \mathrm{mincut}(a_{i-1},a_i)\)

\(\mathrm{mincut}(S,T)\) 表示图中 \(S\) 为源点,\(T\) 为汇点的最小割。

求最大的收益,并输出方案。

$2\le n\le 200 $ , $ 1\le m\le 1000$


对无向图建出来最小割树,现在问题转化为了定义树上两点路径权值是路径上的边权最小值,构造一个排列,使得相邻两点的路径权值和最大。

我们考虑对当前的一棵树,取出边权最小的一条边,那么我们一定要只经过这条边一次,于是去掉这条边就变成了两个子问题,分治下去就可以构造出来了。

构造答案这部分实现的好是可以做到 \(O(n\log n\alpha(n))\) 的,而且我写的也是这种。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 200;
const int inf = 1e9;
using namespace std;
struct edges
{
	int u,v,w;
}edge[N + 5];
int n,m,t[N + 5],t1[N + 5],t2[N + 5],e1[N + 5],e2[N + 5],e[N + 5],edge_cnt,fa[N + 5],a[N + 5],ans;
namespace F
{
    const int N = 3e6;
    const long long inf = 2e18;
    struct edges
    {
        int to;
        long long cost;
    }edge[N * 2 + 5],e[N * 2 + 5];
    int nxt[N * 2 + 5],head[N + 5],edge_cnt = 1,dis[N + 5],q[N + 5],cur[N + 5],S,T;
    void add_edge(int u,int v,long long w)
    {
        edge[++edge_cnt] = (edges){v,w};
        nxt[edge_cnt] = head[u];
        head[u] = edge_cnt;
    }
    void add(int u,int v,long long w)
    {
        add_edge(u,v,w);
        add_edge(v,u,w);
    }
    int bfs()
    {
        for (int i = 1;i <= n;i++)
            cur[i] = head[i],dis[i] = 0;
        int l = 1,r = 0;
        dis[S] = 1;
        q[++r] = S;
        while (l <= r)
        {
            int u = q[l++];
            for (int i = head[u];i;i = nxt[i])
            {
                int v = edge[i].to,w = edge[i].cost;
                if (w && !dis[v])
                {
                    dis[v] = dis[u] + 1;
                    q[++r] = v;
                }
            }
        }
        return dis[T];
    }
    long long dfs(int u,long long flow)
    {
        if (u == T)
            return flow;
        long long sm = 0;
        for (int &i = cur[u];i;i = nxt[i])
        {
            int v = edge[i].to;
            long long w = edge[i].cost;
            if (dis[v] == dis[u] + 1 && w)
            {
                long long res = dfs(v,min(w,flow));
                edge[i].cost -= res;
                edge[i ^ 1].cost += res;
                sm += res;
                flow -= res;
                if (!flow)
                    break;
            }
        }
        return sm;
    }
    void init()
    {
    	for (int i = 2;i <= edge_cnt;i++)
    		e[i] = edge[i];
    }
    void clear()
    {
        for (int i = 2;i <= edge_cnt;i++)
        	edge[i] = e[i];
    }
    long long dinic(int s,int t)
    {
        S = s;T = t;
        clear();
        long long ans = 0;
        while (bfs())
            ans += dfs(S,inf);
        return ans;
    }
}
int find(int x)
{
	if (fa[x] == x)
		return x;
	return fa[x] = find(fa[x]);
}
void build(int l,int r)
{
	if (l >= r)
		return;
	int val = F::dinic(t[l],t[r]),cnt1 = 0,cnt2 = 0;
	edge[++edge_cnt] = (edges){t[l],t[r],val};
	for (int i = l;i <= r;i++)
		if (F::dis[t[i]])
			t1[++cnt1] = t[i];
		else
			t2[++cnt2] = t[i];
	for (int i = 1;i <= cnt1;i++)
		t[i] = t1[i];
	for (int i = 1;i <= cnt2;i++)
		t[i + cnt1] = t2[i];
	build(1,cnt1);
	build(cnt1 + 1,cnt2 + cnt1);
}
void solve(int l,int r,int ql,int qr)
{
	if (l > r)
		return;
	if (l == r)
	{
		a[l] = t[l];
		return;
	}
	for (int i = l;i <= r;i++)
		fa[t[i]] = t[i];
	int mi = inf,id = 0,cnt1 = 0,cnt2 = 0,q1 = 0,q2 = 0;
	for (int i = ql;i <= qr;i++)
		if (edge[e[i]].w < mi)
			mi = edge[e[i]].w,id = e[i];
	ans += mi;
	for (int i = ql;i <= qr;i++)
		if (e[i] != id)
			fa[find(edge[e[i]].u)] = find(edge[e[i]].v);
	int x = find(edge[id].u);
	for (int i = l;i <= r;i++)
	{
		if (find(t[i]) == x)
			t1[++cnt1] = t[i];
		else
			t2[++cnt2] = t[i];
	}
	for (int i = ql;i <= qr;i++)
		if (e[i] != id)
		{
			if (find(edge[e[i]].u) == x)
				e1[++q1] = e[i];
			else
				e2[++q2] = e[i];
		}
	for (int i = 1;i <= cnt1;i++)
		t[i + l - 1] = t1[i];
	for (int i = 1;i <= cnt2;i++)
		t[l + i + cnt1 - 1] = t2[i];
	for (int i = 1;i <= q1;i++)
		e[i + ql - 1] = e1[i];
	for (int i = 1;i <= q2;i++)
		e[i + q1 + ql - 1] = e2[i];
	solve(l,l + cnt1 - 1,ql,ql + q1 - 1);
	solve(l + cnt1,r,ql + q1,ql + q1 + q2 - 1);
}
int main()
{
	scanf("%d%d",&n,&m);
	int u,v,w;
	for (int i = 1;i <= m;i++)
	{
		scanf("%d%d%d",&u,&v,&w);
		F::add(u,v,w);
	}
	F::init();
	for (int i = 1;i <= n;i++)
		t[i] = i;
	build(1,n);
	for (int i = 1;i <= n;i++)
		t[i] = i;
	for (int i = 1;i < n;i++)
		e[i] = i;
	solve(1,n,1,n - 1);
	cout<<ans<<endl;
	for (int i = 1;i <= n;i++)
		printf("%d ",a[i]);
	return 0;
}
posted @ 2021-03-29 17:26  eee_hoho  阅读(63)  评论(0编辑  收藏  举报