ZJOI2011最小割 最小割树

题目描述

小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: ”对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分是关于s,t的割。

对于带权图来说,将所有顶点处在不同部分的边的权值相加所得到的值定义为这个割的容量,而s,t的最小割指的是在关于s,t的割中容量最小的割“

现给定一张无向图,小白有若干个形如”图中有多少对点它们的最小割的容量不超过x呢“的疑问,小蓝虽然很想回答这些问题,但小蓝最近忙着挖木块,于是作为仍然是小蓝的好友,你又有任务了。
输入输出格式
输入格式:

输入文件第一行有且只有一个正整数T,表示测试数据的组数。 对于每组测试数据, 第一行包含两个整数n,m,表示图的点数和边数。下面m行,每行3个正整数u,v,c(1<=u,v<=n,0<=c<=106),表示有一条权为c的无向边(u,v) 接下来一行,包含一个整数q,表示询问的个数 下面q行,每行一个整数x,其含义同题目描述。

输出格式:

对于每组测试数据,输出应包括q行,第i行表示第i个问题的答案。对于点对(p,q)和(q,p),只统计一次(见样例)。两组测试数据之间用空行隔开。

分析:
1.貌似这类要求的东西很多的题目都往分治方面去想。
2.设S1-T1的割集为C1,S2-T2的割集为C2,则,C1和C2必定不是跨立的(一定为包含关系或没有交集)。那么一共就只有n-1个本质不同的割。
3.所以我们的具体做法是分治处理——先任意取S和T做网络流,那么原图就分成了与S相连的部分S’和与T相连的部分T’,此时分别在S’T’中的点对的最小割就是S和T的最小割。再递归并更新答案即可。
4.注意在每次网络流之前一定要把网络还原为初始网络。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const int maxn=160;
const int maxm=6100;
const int INF=1e9;
int to[maxm],Next[maxm],Begin[maxn],w[maxm],e;
int dis[maxn],gap[maxn],d[maxn];
int n,m;
int S,T,tot;
void add(int x,int y,int z){
    to[++e]=y;
    Next[e]=Begin[x];
    Begin[x]=e;
    w[e]=z;
}
bool bfs(){
    memset(dis,0,sizeof(dis));
    queue<int>q;
    q.push(S);dis[S]=1;
    int v;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=Begin[u];i;i=Next[i])if(w[i]>0 && (!dis[v=to[i]])){
            dis[v]=dis[u]+1;
            q.push(v);
        }
    }
    return dis[T];
}
int cur[maxn];
int dfs(int x,int flow){
    if(x==T) return flow;
    int v,tmp,ret=0;
    for(int &i=cur[x];i;i=Next[i]){if(w[i]>0 && dis[v=to[i]]==dis[x]+1)
        if((tmp=dfs(v,min(flow,w[i])))){
            w[i]-=tmp;w[i^1]+=tmp;
            flow-=tmp;ret+=tmp;
        }
        if(!flow) return ret;
    }
    return ret;
}
int cut[maxn][maxn];
int Maxflow(int s,int t){
    S=s,T=t;
    int maxflow=0;
    while(bfs()){
        for(int i=1;i<=n;i++) cur[i]=Begin[i];
        maxflow+=dfs(S,INF);
    }
    return maxflow;
}
int id[maxn],tmp[maxn];
int w1[maxm];
void solve(int L,int R){
    if(L==R) return;
    for(int i=2;i<=e;i++) w[i]=w1[i];
    int ret=Maxflow(id[L],id[R]),l=L,r=R;
    for(int i=1;i<=n;i++)if(dis[i]){
        for(int j=1;j<=n;j++)if(!dis[j]) cut[i][j]=cut[j][i]=min(cut[i][j],ret);
    }
    for(int i=L;i<=R;i++) tmp[dis[id[i]]?l++:r--]=id[i];
    for(int i=L;i<=R;i++) id[i]=tmp[i];
    solve(L,r);solve(l,R);
}
int main(){
    int kase;
    scanf("%d",&kase);
    while(kase--){
        e=1;
        scanf("%d%d",&n,&m);
        memset(Begin,0,sizeof(Begin));
        for(int i=1;i<=n;i++) id[i]=i;
        for(int i=1;i<=m;i++){
            int u,v,c;
            scanf("%d%d%d",&u,&v,&c);
            add(u,v,c);add(v,u,c);
        }
        for(int i=2;i<=e;i++) w1[i]=w[i];
        memset(cut,0x7f,sizeof(cut));
        solve(1,n);
        int q;
        scanf("%d",&q);
        while(q--){
            int p,ans=0;
            scanf("%d",&p);
            for(int i=1;i<=n;i++)
                for(int j=i+1;j<=n;j++)
                    if(cut[i][j]<=p) ans++;
            printf("%d\n",ans);
        }
        if(kase) puts("");
    }
    return 0;
}

^_^

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值