BZOJ 2229 [Zjoi2011]最小割

最小割树

关于最小割树的详细内容和证明,请看【ZJOI2016 day2讲课 无向图最小割】,这个东西自己找吧,我就不发了。。。

默默摘抄一些Gusfield算法

fa[u]在u被枚举到之前表示u所属点集的代表点,枚举到之后表示u在最小割树上的父亲。初始时所有点都属于以1为代表点的点集。从2到|V|依次枚举u,把u所在点集以u和fa[u]为源和汇拆分,所有被分到u侧的点x把fa[x]修改为u。

注意枚举之后只能把fa[x]=u或fa[x]=fa[u]的x拿来改,即属于当前点集的拿来改。这是小细节,不然会挂很惨。。。

关于跑完一次最大流之后如何确定最小割:找出从S开始在残量网络上BFS到的点和没被BFS到的点,这两个点集之间就是最小割

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 155
#define M 3005
using namespace std;
namespace runzhe2000
{
    const int INF = 1<<29;
    int n, m, ecnt = 1, last[N], cur[N], level[N], fa[N], fa_v[N], S, T, qx[N], arr[N*2], cnt[N*2];
    struct edge{int next, to, flow;}e[M<<2];
    void addedge(int a, int b, int c)
    {
        e[++ecnt] = (edge){last[a], b, c};
        last[a] = ecnt;
        e[++ecnt] = (edge){last[b], a, 0};
        last[b] = ecnt;
    }
    void linkedge(int a, int b, int c)
    {
        e[++ecnt] = (edge){last[a], b, c};
        last[a] = ecnt;
        e[++ecnt] = (edge){last[b], a, c};
        last[b] = ecnt;
    } 

    int q[N], timer, vis[N];
    bool bfs()
    {
        level[q[0] = S] = 1; vis[S] = ++timer;
        for(int head=0, tail=1; head<tail; head++)
        {
            int x = q[head];
            for(int i = last[x]; i; i = e[i].next)
            {
                int y = e[i].to;
                if(vis[y] == timer || !e[i].flow) continue;
                vis[y] = timer; 
                level[y] = level[x] + 1;
                if(y == T)return true;
                q[tail++] = y;
            }
        }
        return false;
    }
    int dfs(int x, int flow)
    {
        if(x == T)return flow;
        int use = 0;
        for(int &i = cur[x]; i; i = e[i].next)
        {
            int y = e[i].to;
            if(level[y] != level[x] + 1)continue;
            int w = dfs(y, min(e[i].flow, flow - use));
            use += w;
            e[i].flow -= w;
            e[i^1].flow += w;
            if(use == flow)return use;
        }
        return use;
    }
    int dinic()
    {
        int ret = 0;
        while(bfs())
        {
            memcpy(cur, last, sizeof(last));
            ret += dfs(S, INF);
        }
        return ret;
    }
    void restore()
    {
        for(int i = 2; i <= ecnt; i += 2) 
            e[i].flow += e[i^1].flow, e[i^1].flow = 0;
    }
    void dfs(int x, int v, int f)
    {
        cnt[v]++;
        for(int i = last[x]; i; i = e[i].next)
        {
            int y = e[i].to;
            if(y == f)continue;
            dfs(y, min(v, e[i].flow), x);
        }
    }
    void init()
    {
        ecnt = 1;
        memset(last,0,sizeof(last));
        memset(cnt,0,sizeof(cnt));
    }
    void main()
    {
        int kase;
        scanf("%d",&kase);
        for(; kase--; )
        {
            init();
            scanf("%d%d",&n,&m);
            for(int i = 1, a, b, c; i <= m; i++)
            {
                scanf("%d%d%d",&a,&b,&c);
                addedge(a,b,c);
                addedge(b,a,c);
            }
            for(int i = 1; i <= n; i++) fa[i] = 1;
            for(int i = 2; i <= n; i++)
            {
                restore();
                S = i;
                T = fa[i];
                fa_v[i] = dinic();  
                for(int j = i+1; j <= n; j++) if(vis[j] == timer && fa[j] == fa[i]) fa[j] = i;
            }
            int Q, tot = 0;
            scanf("%d",&Q);
            for(int i = 1; i <= Q; i++) scanf("%d",&qx[i]), arr[++tot] = qx[i];
            for(int i = 2; i <= n; i++) arr[++tot] = fa_v[i];
            sort(arr+1,arr+1+tot);
            tot = unique(arr+1,arr+1+tot) - arr - 1;
            for(int i = 1; i <= Q; i++) qx[i] = lower_bound(arr+1,arr+1+tot,qx[i]) - arr;
            for(int i = 2; i <= n; i++) fa_v[i] = lower_bound(arr+1,arr+1+tot,fa_v[i]) - arr;
            memset(last,0,sizeof(last)); ecnt = 1;
            for(int i = 2; i <= n; i++) linkedge(fa[i], i, fa_v[i]);
            for(int i = 1; i <= n; i++) dfs(i, N*2-1, 0);
            for(int i = 1; i < N*2; i++)
            {
                cnt[i] /= 2;
                cnt[i] += cnt[i-1];
            }
            for(int i = 1; i <= Q; i++) printf("%d\n",cnt[qx[i]]);
            puts("");
        }
    }
}
int main()
{
    runzhe2000::main();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值