BZOJ4464旅行时的困惑——最小流

时间:2019-01-19
本文章向大家介绍BZOJ4464旅行时的困惑——最小流,主要包括BZOJ4464旅行时的困惑——最小流使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Description
Waldives 有 N 个小岛。目前的交通系统中包含 N-1 条快艇专线,每条快艇专线连接两个岛。这 N-1条快艇专线恰好形成了一棵树。 由于特殊的原因,所有N-1条快艇专线都是单向的。这导致了很多岛屿之间不能相互到达。因此,Waldives 政府希望新建一些公交线路,使得建设完毕后,任意两个小岛都可以互相到达。为了节约开支,政府希望建设最少的公交线路。
同时,出于规划考虑,每一条公交线路都有如下的要求:
1、每一条交通线路包含若干条连续的快艇专线,你可以认为一条公交线路对应树上的一条路径,而其所包含的若干快艇专线则对应树上被这条路径所覆盖的树边(也就是之前已经存在的某个快艇专线);
2、显然一条交通线路只能覆盖树上任意一条边至多一次;
3、公交线路中所包含的每一个快艇专线都是有方向的,并且与其所覆盖的树边的方向相反;
4、不同的公交线路可以覆盖树上相同的点或者相同的边。
Waldives 的 N 个岛屿分别从 0 到 N-1 编号。现在给出 Waldives 已有的快艇专线信息,请计算最少所需要新建的交通线路的数量。

Input
第一行包含一个整数 N。 接下来 N-1行,每行包含两个整数 x,y。表示存在一条从岛屿 x开往岛屿y的快艇专线。 N < = 100000

Output
输出一行一个整数,表示需要建设的最少的交通线路数量。

Sample Input
4

0 1

1 2

1 3
Sample Output
2
HINT
样例如下图所示。图中给出了一个可行的最佳方案。

其中黑色的实边代表原先已经存在的快艇专线, 而虚边则对新加入的公交线路,分别为 2->1->0和3->1->0。

注意直接新建公交线路 3->2 是不允许的,这并不是一条树上的合法路径,因为编号为3的点与编号为1的点在树中并不直接相连。同样的,建立公交路线2->1->3也是不允许的,因为这条路线中包含快艇专线1->3,这并没有和已有的专线的方向相反


这题的意思也就是在反向边上找出最少的路径条数,使得其能覆盖整个路径。
实际上只要求出原图的最小流即可。
显然我们按照原图的方式建图,那么一条路径上整个只会贡献价值为11的流量,两条路径可能会在一部分位置重复相交,那么在这相交的部分贡献的流量就是22,显然这也符合题意(因为会有分岔,至少得有两条路径才能完全覆盖),以此类推……
相当于我们一个流量是代表一条路径的,所以最小流也就代表着最少的路径数。
Ps:这题数据范围极大,不加当前弧优化会TLE。由于我们每次增广时都会取到最大的值,所以相当于该边的价值已经被完全利用了,所以下一次就不用扫这条边了。
if(L[i].val>0) cur[x]=i;      //当前弧优化,具体见下代码
完整代码:
#include<bits/stdc++.h>
#define MAXN 5000005
#define inf 2e9
using namespace std;
int read(){
    char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int n,cnt,head[MAXN],nxt[MAXN],d[MAXN];
int S,T,st,en,dis[MAXN],dfn,vis[MAXN],cur[MAXN];
struct node{
    int to,val;
}L[MAXN];
queue<int> q;
void add(int x,int y,int c){
    L[cnt]=(node){y,c};
    nxt[cnt]=head[x];head[x]=cnt;cnt++;
    L[cnt]=(node){x,0};
    nxt[cnt]=head[y];head[y]=cnt;cnt++;
}
int BFS(){
    dis[st]=1;q.push(st);vis[st]=++dfn;
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i!=-1;i=nxt[i]){
            int to=L[i].to;
            if(vis[to]!=vis[x]&&L[i].val){
                q.push(to);
                dis[to]=dis[x]+1;vis[to]=vis[x];
            }
        }
    }
    for(int i=0;i<=en;i++) cur[i]=head[i];
    return vis[en]==dfn;
}
int DFS(int x,int now){
    if(x==en) return now;
    int res=0;
    for(int i=cur[x];i!=-1&&now;i=nxt[i]){
        int to=L[i].to;
        if(dis[to]==dis[x]+1&&vis[to]==vis[x]&&L[i].val){
            int fd=DFS(to,min(now,L[i].val));
            res+=fd;now-=fd;
            L[i].val-=fd;L[i^1].val+=fd;
            if(L[i].val>0) cur[x]=i;
        }
    }
    if(!res) dis[x]=-1;
    return res;
}
int main()
{
    n=read();S=0;T=n+1;st=n+2;en=n+3;
    memset(head,-1,sizeof(head));
    for(int i=1;i<n;i++){
        int x=read()+1,y=read()+1;
        d[x]--;d[y]++;add(x,y,inf);
    }
    for(int i=1;i<=n;i++){
        add(S,i,inf);add(i,T,inf);
    }
    for(int i=1;i<=n;i++){
        if(d[i]>0){
            add(st,i,d[i]);
        }
        if(d[i]<0){
            add(i,en,-d[i]);
        }
    }
    while(BFS()) DFS(st,inf);
    add(T,S,inf);
    while(BFS()) 
        DFS(st,inf);
    printf("%d",L[cnt-1].val);
    return 0;
}