牛客多校

Minimum-cost Flow

令 cost( f , c) 为每条边的流量为f,寻求c的最大流,那么

cost(u/v,1)=cost(u,v)/v=cost(1,v/u)*u/v

对应着每条边的流量为1,寻求v/u的最大流的情况,

令 v=a*u+b,那么 cost(1,v/u)=sum[a]+b/u*path[a+1]

那么:
cost(u/v,1)

=cost(u,v)/v

=cost(1,v/u)*u/v

=u*sum[a]+b*path[a+1]  / v

当流完p条增广路之后,p *u/v <1 的时候,即为无解情况。

#include <bits/stdc++.h>
#include <algorithm>
using namespace std;
typedef long long ll;
int n,m,S,T;
ll maxflow,mincost;
vector<ll>path;
const int N=1e5+50;
const int inf=0x3f3f3f3f;
inline ll gcd(ll a,ll b) {
    if(a%b==0) return b;
        else return (gcd(b,a%b));
}
struct Dinic{
    int head[N],dep[N];ll dis[N],pre[N];ll flow[N],last[N];
    //dep记录分层图,pre点前驱,flow点的流,last点的前一条边,dis表示费用
    bool inq[N];
    int ecnt; 
    struct edge{
        int v;long long w,flow;int next;
    }e[N*10];
    void init(){
        memset(head, -1, sizeof head);
        ecnt = 0;
    }
    void addedge(int u,int v,ll w,ll flow){
    e[ecnt].v=v;e[ecnt].w=w;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++;
    e[ecnt].v=u;e[ecnt].w=-w;e[ecnt].flow=0;e[ecnt].next=head[v];head[v]=ecnt++;
    }
    bool spfa(){//spfa找增广路
        memset(dis,inf,sizeof dis);
        memset(flow,inf,sizeof flow);
        memset(inq,0,sizeof inq);
        queue<int>Q;
        Q.push(S);inq[S]=1;dis[S]=0;pre[T]=-1;
        while(!Q.empty()){
            int u=Q.front();Q.pop();inq[u]=0;
            for(int i=head[u];~i;i=e[i].next){
                int v=e[i].v,w=e[i].w;
                if(e[i].flow>0&&dis[v]>dis[u]+w){
                    dis[v]=dis[u]+w;pre[v]=u;last[v]=i;
                    flow[v]=min(flow[u],e[i].flow);
                    if(!inq[v]){
                    Q.push(v);inq[v]=1;
                    }
                }
            }
        }
        return pre[T]!=-1;
    }
    void  MCMF(){
     maxflow=mincost=0;
        while(spfa()){//推进新流
            int cur=T;
            maxflow+=flow[T];
            mincost+=flow[T]*dis[T];
            path.push_back(dis[T]);
            while(cur!=S){//汇点向前回溯
                e[last[cur]].flow-=flow[T];
                e[last[cur]^1].flow+=flow[T];
                cur=pre[cur];
            }
        }

    }
}dinic;  
ll sum[N];
int  main(){
    while(~scanf("%d %d",&n,&m)){
        dinic.init();path.clear();
        S=1;T=n;
        for(ll i=1,u,v,w;i<=m;i++){
            scanf("%lld %lld %lld",&u,&v,&w);
            dinic.addedge(u,v,w,1);
        }
        dinic.MCMF();
        ll p=path.size();sum[0]=0;
        for(int i=1;i<=p;i++)sum[i]=sum[i-1]+path[i-1];
        int Q;scanf("%d",&Q);
        while(Q--){
            ll u,v;scanf("%lld %lld",&u,&v);
            // cout<<"ans :";

            if(p*u<v){puts("NaN");continue;}
            ll a=v/u,b=v%u;
            ll ans=u*sum[a]+b*path[a];
            ll  d=gcd(ans,v);
            ans/=d;v/=d;
            printf("%lld/%lld\n",ans,v);
        }
    }   


    return 0;
}
View Code

 

1 or 2

考虑最初为有向边,每个点构造有向边必然使得邻居度数加一,

将每个点与源点和汇点连一条流量为di的边,当有一条有向边的时候,将u和v的左右点互连,判断是否满流即可。

当且仅当原度序列与握手定理矛盾,最大流不成立。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=1e6+5;
int n,m,S,T;
struct Dinic{
    int head[N],dep[N];
    int ecnt;
    struct edge{
        int v,flow,next;
    }e[N*10];
 
    void init(){
        memset(head, -1, sizeof head);
        ecnt = 0;
    }
 
    void addedge(int u, int v, int  flow) {
    e[ecnt].v=v;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++;
    e[ecnt].v=u;e[ecnt].flow=0;e[ecnt].next=head[v];head[v]=ecnt++;
    }
 
    bool BFS(){//分层图找增广路
        memset(dep,0,sizeof dep);
        queue<int>Q;
        Q.push(S);dep[S]=1;
        while(!Q.empty()){
            int u=Q.front();Q.pop();
            for(int i=head[u];~i;i=e[i].next){
                if(e[i].flow&&!dep[e[i].v]){
                    dep[e[i].v]=dep[u]+1;
                    Q.push(e[i].v);
                }
            }
        }
        return dep[T];
    }
 
    int DFS(int u, int f){//推进新流
        if(u==T||f==0)return f;
        int w,used=0;
        for(int i=head[u];~i;i=e[i].next){
            if(e[i].flow&&dep[e[i].v]==dep[u]+1){
                w=DFS(e[i].v,min(f,e[i].flow));//多路增广
                e[i].flow-=w;e[i^1].flow+=w;
                used+=w;f-=w;
            }
        }
        if(!used)dep[u]=-1;//炸点优化
        return used;
    }
    int maxflow() {
        int ans=0;
        while(BFS()){
            ans+=DFS(S,inf);
        }
        return ans;
    }
}dinic;
int  d[N];
int main(){
    while(~scanf("%d %d",&n,&m)){
        dinic.init();S=0;T=2*n+2*m+10;
        int tot=0;

        for(int i=1;i<=n;i++){
            scanf("%d",&d[i]);
            dinic.addedge(S,i,d[i]);
            dinic.addedge(i+n+2*m,T,d[i]);
            tot+=d[i];
        }

        for(int i=1,u,v;i<=m;i++){
        scanf("%d %d",&u,&v);
        dinic.addedge(u,i+n,1);dinic.addedge(v,i+n,1);
        dinic.addedge(i+n,i+m+n,2);
        dinic.addedge(i+m+n,n+2*m+u,1);dinic.addedge(i+m+n,n+2*m+v,1);
        }
        if(dinic.maxflow()>=tot)puts("Yes");
        else puts("No");
    }
    // system("pause");
    return 0;
}
View Code

 

Operating on a Graph

题意:一张图,最初每个点属于gi集合,Q次询问,每次将与Oi有邻接边的集合加入Oi中,问Q次操作后每个点的所属情况。

用一个表储存每个点的邻居,那么每次合并的时候,将邻居的邻接表加入当前的邻接表中,把邻居的并查集指向u,

如果遇到fa[i]!=i,说明该集合不存在,直接跳过,

复杂度玄学。

#include<bits/stdc++.h>
using namespace std;
const int N=8e5+500;
int fa[N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
// void build(int x,int y){
//     int dx=find(x),dy=find(y);
//     if(dx!=dy)fa[dx]=dy;
// }
int n,m;
vector<int>G[N];
void init(){
    for(int i=0;i<n;i++)fa[i]=i,G[i].clear();
}
void add(int u,int v){
    G[u].push_back(v);
    G[v].push_back(u);
}
void merge(vector<int>&a,vector<int>&b){
    if(a.size()<b.size())swap(a,b);
    for(int i=0;i<b.size();i++)a.push_back(b[i]);
}
void work(int x){
    if(fa[x]!=x)return ;
    // vector<int>cur=G[x];
    // G[x].clear();
    for(int i=0;i<G[x].size();i++){
        int v=G[x][i];
        int fv=find(v);
        if(fv==x)continue;
        // build(fv,x);
        fa[fv]=x;
        merge(G[x],G[fv]);
    }

}
int main(){
    int t;scanf("%d",&t);
    while(t--){
     scanf("%d %d",&n,&m);
        init();

    while(m--){
        int u,v;scanf("%d %d",&u,&v);
        add(u,v);
    }

    int Q;scanf("%d",&Q);

    while(Q--){
    int x;scanf("%d",&x);
    work(x);
    }
    // cout<<"ans :";
    for(int i=0;i<n;i++)printf("%d ",find(i));
        cout<<endl;
    }
    // system("pause");
    return 0;
}

// 8 9
// 5 9 8 7 2 3 4 1
View Code
 
Interval
考虑区间的放大和缩小实际上可以转换为网格上对应的向左向右向上向下移动,那么原问题即为:在一个网格图上求一个最小割,
将原图的网格边连起来,将对角线上的点与汇点连一条流量为inf 的边,表示边不可删除,那么原问题即为网格图的对偶图的最短路
 
最小割TLE代码,1e6个点,实际上跑不出来。
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+70000;
typedef long long ll;
const ll INF=1e17;
int n,m,S,T;
struct Dinic{
    int head[N],dep[N];
    int ecnt;
    struct edge{
        int v,next;ll flow;
    }e[N*10];
 
    void init(){
        memset(head, -1, sizeof head);
        ecnt = 0;
    }
 
    void addedge(int u, int v, ll flow) {
    e[ecnt].v=v;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++;
    e[ecnt].v=u;e[ecnt].flow=flow;e[ecnt].next=head[v];head[v]=ecnt++;
    }
 
    bool BFS(){//分层图找增广路
        memset(dep,0,sizeof dep);
        queue<int>Q;
        Q.push(S);dep[S]=1;
        while(!Q.empty()){
            int u=Q.front();Q.pop();
            for(int i=head[u];~i;i=e[i].next){
                if(e[i].flow&&!dep[e[i].v]){
                    dep[e[i].v]=dep[u]+1;
                    Q.push(e[i].v);
                }
            }
        }
        return dep[T];
    }
 
    ll DFS(int u,ll f){//推进新流
        if(u==T||f==0)return f;
        ll w,used=0;
        for(int i=head[u];~i;i=e[i].next){
            if(e[i].flow&&dep[e[i].v]==dep[u]+1){
                w=DFS(e[i].v,min(f,e[i].flow));//多路增广
                e[i].flow-=w;e[i^1].flow+=w;
                used+=w;f-=w;
            }
        }
        if(!used)dep[u]=-1;//炸点优化
        return used;
    }
    ll  maxflow() {
        ll ans=0;
        while(BFS()){
            ans+=DFS(S,INF);
        }
        return ans;
    }
}dinic; 
int get(int x,int y){
    return (x-1)*n+y;
}
map<int,map<int,int> >vis;
int main(){
    scanf("%d %d",&n,&m);
    dinic.init();   
    while(m--){
        ll l,r,c;char dir;
        scanf("%lld %lld %c %lld",&l,&r,&dir,&c);
        int u=get(l,r);
         
        if(dir=='L'){
            vis[get(l+1,r)][get(l,r)]=vis[get(l,r)][get(l+1,r)]=1;
            dinic.addedge(get(l,r),get(l+1,r),c);
        }
 
        else {
           vis[get(l,r-1)][get(l,r)]=vis[get(l,r)][get(l,r-1)]=1;
            dinic.addedge(get(l,r),get(l,r-1),c);
        }
    }
 
    S=0;T=get(n,1);
    dinic.addedge(S,get(1,n),INF);
    for(int i=1;i<=n;i++)dinic.addedge(get(i,i),T,INF);
     
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            int u=get(i,j),v=get(i+1,j);
            if(!vis[u][v])dinic.addedge(u,v,INF),vis[u][v]=vis[v][u]=1;
            v=get(i,j-1);
            if(!vis[u][v])dinic.addedge(u,v,INF),vis[u][v]=vis[v][u]=1;
       }
    }
 
    // cout<<INF<<endl;
    ll ans=dinic.maxflow();
    if(ans!=INF)cout<<ans<<endl;
    else cout<<"-1"<<endl;
    // system("pause");
    return 0;
}
View Code

转化为对偶图最短路:

 

一种哈希的方法是:把每个对偶图的点映射到原网格图左上方的点,当x<=0,即为源点,y > = n 即为汇点,求一个最短路,复杂度O(N)
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+500;
typedef long long ll;
const ll inf=1e17;
ll S,T;
int head[N],ecnt;//cnt main里初始化为0,head main初始化为-1
struct edge{int v;ll w;int next;}e[N*10];//注意有重边,乘10
struct node{int id;ll dis;//id点,dis距离
    node(int x,ll y){id=x,dis=y;}
    friend bool operator<(node a,node b){
    return a.dis>b.dis;//距离小的出队
    }
};
void add(int u,int v,ll w){//加边
    e[ecnt].v=v,e[ecnt].w=w,e[ecnt].next=head[u],head[u]=ecnt++;
    e[ecnt].v=u,e[ecnt].w=w,e[ecnt].next=head[v],head[v]=ecnt++;
}
ll dis[N];
ll n,m;
bool done[N];//dis记录距离,done记录是否找到最短路
void dijkstra(int s){
    for(int i=0;i<N;i++)dis[i]=inf,done[i]=0;
    dis[s]=0;
    priority_queue<node>Q;
    Q.push(node(s,0));
    while(!Q.empty()){
        int u=Q.top().id;Q.pop();
        if(done[u])continue;
        done[u]=1;
        for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(done[v])continue;
        if(dis[v]>dis[u]+e[i].w){
            dis[v]=dis[u]+e[i].w;//更新距离
            Q.push(node(v,dis[v]));
            }       
        }
    }
}
int Hash(int x,int y){
    if(x<=0)return S;
    if(y>=n)return T;
    return (x-1)*n+y;
}
int main(){
 
    memset(head,-1,sizeof head);ecnt=0;
    cin>>n>>m;
    S=n*n/2+10;T=n*n/2+20;
    while(m--){
        int l,r;ll c;char dir;
        scanf("%d %d %c %lld",&l,&r,&dir,&c);
        if(dir=='L'){
            add(Hash(l,r),Hash(l,r-1),c);
        }
        else {
            add(Hash(l-1,r-1),Hash(l,r-1),c);
        }
    }
    dijkstra(S);
    if(dis[T]>=inf)puts("-1");
    else cout<<dis[T]<<endl;
    // system("pause");
    return 0;
}
View Code

 

posted @ 2020-07-18 23:47  无声-黑白  阅读(149)  评论(0编辑  收藏  举报