• BZOJ2561: 最小生成树


    Description

        给定一个边带正权的连通无向图G=(V,E),其中N=|V|,M=|E|,N个点从1到N依次编号,给定三个正整数u,v,和L (u≠v),假设现在加入一条边权为L的边(u,v),那么需要删掉最少多少条边,才能够使得这条边既可能出现在最小生成树上,也可能出现在最大生成树上?

    Input

      第一行包含用空格隔开的两个整数,分别为N和M;
      接下来M行,每行包含三个正整数u,v和w表示图G存在一条边权为w的边(u,v)。
      最后一行包含用空格隔开的三个整数,分别为u,v,和 L;
      数据保证图中没有自环。

    Output

     输出一行一个整数表示最少需要删掉的边的数量。

    Sample Input

    3 2
    3 2 1
    1 2 3
    1 2 2

    Sample Output

    1

    HINT

    对于20%的数据满足N ≤ 10,M ≤ 20,L ≤ 20;

    对于50%的数据满足N ≤ 300,M ≤ 3000,L ≤ 200;

    对于100%的数据满足N ≤ 20000,M ≤ 200000,L ≤ 20000。

    我们考虑什么时候特殊边(u,v,l)不可能出现在最小生成树上。

    由Kruskal不难发现,当有一条从u到v的路径满足路径上每一条边都<l则不可行。

    那么这其实就是一个最小割模型。

    类似再算一遍最大生成树就行了。

    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define ren for(int i=first[x];i!=-1;i=next[i])
    using namespace std;
    const int BufferSize=1<<16;
    char buffer[BufferSize],*head,*tail;
    inline char Getchar() {
        if(head==tail) {
            int l=fread(buffer,1,BufferSize,stdin);
            tail=(head=buffer)+l;
        }
        return *head++;
    }
    inline int read() {
        int x=0,f=1;char c=Getchar();
        for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=Getchar()) x=x*10+c-'0';
        return x*f;
    }
    const int maxn=20010;
    const int maxm=400010;
    const int inf=1e9;
    struct Dinic {
        int n,m,s,t,clo,cur[maxn],vis[maxn],d[maxn];
        int first[maxn],next[maxm];
        struct Edge {int from,to,flow;}edges[maxm];
        void init(int n) {
            this->n=n;m=0;
            memset(first,-1,sizeof(first));
        }
        void AddEdge(int u,int v,int w) {
            edges[m]=(Edge){u,v,w};next[m]=first[u];first[u]=m++;
            edges[m]=(Edge){v,u,w};next[m]=first[v];first[v]=m++;
        }
        int Q[maxn];
        int bfs() {
            int l=1,r=1;Q[l]=s;vis[s]=++clo;
            while(l<=r) {
                int x=Q[l++];cur[x]=first[x];
                ren {
                    Edge& e=edges[i];
                    if(e.flow&&vis[e.to]!=clo) {
                        vis[e.to]=clo;
                        d[e.to]=d[x]+1;
                        Q[++r]=e.to;
                    }
                }
            }
            return vis[t]==clo;
        }
        int dfs(int x,int a) {
            if(x==t||!a) return a;
            int flow=0,f;
            for(int& i=cur[x];i!=-1;i=next[i]) {
                Edge& e=edges[i];
                if(d[e.to]==d[x]+1&&(f=dfs(e.to,min(a,e.flow)))) {
                    e.flow-=f;edges[i^1].flow+=f;
                    flow+=f;a-=f;if(!a) break;
                }
            }
            return flow;
        }
        int solve(int s,int t) {
            this->s=s;this->t=t;int flow=0;
            while(bfs()) flow+=dfs(s,inf);
            return flow;
        }
    }sol;
    int u[maxm],v[maxm],w[maxm];
    int main() {
        int n=read(),m=read();
        rep(i,1,m+1) u[i]=read(),v[i]=read(),w[i]=read();
        sol.init(n);
        rep(i,1,m) if(w[i]>w[m+1]) sol.AddEdge(u[i],v[i],1);
        int ans=sol.solve(u[m+1],v[m+1]);
        sol.init(n);
        rep(i,1,m) if(w[i]<w[m+1]) sol.AddEdge(u[i],v[i],1);
        printf("%d
    ",ans+sol.solve(u[m+1],v[m+1]));
        return 0;
    }
    View Code
  • 相关阅读:
    PHP定时任务实现(计划任务 vs node.js)
    第三方支付,代支付接口调用
    iframe调用页面中的局部部分
    树状数据删除(TP5)
    PHP 代码编写注意事项总结归纳
    MySQL 存储过程与事物
    radio与checkbox的选中事件
    简单十步让你全面理解SQL
    生成条形码
    使2个div 在一行上显示
  • 原文地址:https://www.cnblogs.com/wzj-is-a-juruo/p/5017248.html
Copyright © 2020-2023  润新知