网络流杂题选做

最近总是不知不觉地就做到了网络流的题,感觉知识树要点偏。算了,先随便写点东西再说。

[AHOI2009]最小割

题面

显然一条边至少要满流才有可能是被割边。

先考虑存在性问题,若一条边为某个最小割割边集中的边,那么这条边一定是该最小割中无法取代的。对于一条流量为 \(0\) 的边 \((u,v)\),如果在残量网络上存在从 \(u\)\(v\) 的路径,即在残量网络上 \(u,v\) 属于同一连通块(因为 \((v,u)\) 这条边必然在残量网络中),那么可以从这条路径中分点流量到另一条路径,就不存在一个最小代价路径切断方案,其中该道路被切断。

再考虑必要性问题,先给出结论,当且仅当 \(u\)\(S\) 在同一强连通分量中,同时 \(v\)\(T\) 在同一强连通分量中。因为一条边如果没满流,那么残量网络上必然同时存在原边和反边,两端点同属一强连通分量。考虑缩点后的 DAG,从 \(S\)\(T\) 的路径上如果有不止一条边,那么没有一条边是必然被切断的。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pdi pair<double,int>
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define eps 1e-9
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
            }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=1e4+10,M=1e5+10,inf=INT_MAX;
int n,m,S,T,tot=1,head[N];
struct edge{
    int u,v,nxt,w;
}e[M<<2];
void add(int u,int v,int w){
    e[++tot].v=v;
    e[tot].u=u;
    e[tot].w=w;
    e[tot].nxt=head[u];
    head[u]=tot;
}
int dep[N],cur[N];
bool bfs(int S,int T){
    for(int i=1;i<=n;i++){
        dep[i]=0;
    }
    dep[S]=1;
    queue<int>q;
    q.push(S);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].v,w=e[i].w;
            if(!dep[v]&&w){
                dep[v]=dep[u]+1;
                if(v==T){
                    return 1;
                }
                q.push(v);
            }
        }
    }
    return 0;
}
int dfs(int u,int flow,int T){
    if(u==T){
        return flow;
    }
    int s=0;
    for(int i=cur[u];i;i=e[i].nxt){
        cur[u]=i;
        int v=e[i].v,w=e[i].w;
        if(dep[v]==dep[u]+1&&w){
            int res=dfs(v,min(flow,w),T);
            s+=res;
            flow-=res;
            e[i].w-=res;
            e[i^1].w+=res;
        }
        if(!flow){
            break;
        }
    }
    if(!s){
        dep[u]=0;
    }
    return s;
}
int dfn[N],low[N],col[N],idx,col_num,stk[N],top,in_stack[N];
void tarjan(int u){
    low[u]=dfn[u]=++idx;
    stk[++top]=u;
    in_stack[u]=1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].v,w=e[i].w;
        if(!w){
            continue;
        }
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(in_stack[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u]){
        col[u]=++col_num;
        in_stack[u]=0;
        while(stk[top]!=u){
            col[stk[top]]=col_num;
            in_stack[stk[top]]=0;
            top--;
        }
        top--;
    }
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    read(n),read(m),read(S),read(T);
    for(int i=1;i<=m;i++){
        int u,v,w;
        read(u),read(v),read(w);
        add(u,v,w);
        add(v,u,0);
    }
    while(bfs(S,T)){
        for(int i=1;i<=n;i++){
            cur[i]=head[i];
        }
        dfs(S,inf,T);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]){
            tarjan(i);
        }
    }
    for(int i=1;i<=m;i++){
        if(!e[i<<1].w){
            int u=e[i<<1].u,v=e[i<<1].v;
            if(col[u]!=col[v]){
                write_space(1);
            }
            else{
                write_space(0);
            }
            if(col[u]==col[S]&&col[v]==col[T]){
                write_endl(1);
            }
            else{
                write_endl(0);
            }
        }
        else{
            puts("0 0");
        }
    }
    return 0;
}

[NOI2009] 植物大战僵尸

题面

我们将一个植物前面的植物视作也保护它的植物。因为要击溃一个植物,必须击溃保护它的植物。所以,从保护的植物连边到被保护的植物。但是可能有环,环上的植物是不可能被击溃,所以做一遍拓扑排序,只有被遍历过的植物才有可能是被击溃的植物。我们只保留遍历过的植物,和边上的两个植物都遍历过的边。那么最后题目就变成了求这个图的最大权闭合子图。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pdi pair<double,int>
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define eps 1e-9
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
            }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=610,M=1e6+10,inf=1e9 ;
int n,m,val[N];
int id(int x,int y){
    return (x-1)*m+y;
}
vector<int>G[N],out[N];
int deg[N],vis[N],S,T,tot=1,head[N],ans;
void topo(){
    queue<int>q;
    for(int i=1;i<=n*m;i++){
        if(!deg[i]){
            q.push(i);
        }
    }
    while(!q.empty()){
        int u=q.front();
        vis[u]=1;
        q.pop();
        for(auto v:G[u]){
            deg[v]--;
            if(!deg[v]){
                q.push(v);
            }
        }
    }
}
struct node{
    int v,w,nxt;
}e[M<<1];
void add(int u,int v,int w){
    e[++tot].v=v;
    e[tot].w=w;
    e[tot].nxt=head[u];
    head[u]=tot;
}
int dep[N],cur[N];
bool bfs(int S,int T){
    for(int i=1;i<=T;i++){
        dep[i]=0;
    }
    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].nxt){
            int v=e[i].v,w=e[i].w;
            if(w&&!dep[v]){
                dep[v]=dep[u]+1;
                if(v==T){
                    return 1;
                }
                q.push(v);
            }
        }
    }
    return 0;
}
int dfs(int u,int flow,int T){
    if(u==T){
        return flow;
    }
    int s=0;
    for(int i=cur[u];i;i=e[i].nxt){
        int v=e[i].v,w=e[i].w;
        if(w&&dep[v]==dep[u]+1){
            int res=dfs(v,min(flow,w),T);
            e[i].w-=res;
            e[i^1].w+=res;
            s+=res;
            flow-=res;
        }
        if(!flow){
            break;
        }
    }
    if(!s){
        dep[u]=0;
    }
    return s;
}
int dinic(int S,int T){
    int sum=0;
    while(bfs(S,T)){
        for(int i=1;i<=T;i++){
            cur[i]=head[i];
        }
        sum+=dfs(S,inf,T);
    }
    return sum;
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    read(n),read(m);
    S=n*m+1,T=n*m+2;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            read(val[id(i,j)]);
            int sum;
            read(sum);
            for(int k=1,x,y;k<=sum;k++){
                read(x),read(y);
                x++,y++;
                out[id(i,j)].pb(id(x,y));
                G[id(i,j)].pb(id(x,y));
                deg[id(x,y)]++;
            }
            if(j<m){
                out[id(i,j+1)].pb(id(i,j));
                G[id(i,j+1)].pb(id(i,j));
                deg[id(i,j)]++;
            }
        }
    }
    topo();
    for(int i=1;i<=n*m;i++){
        if(!vis[i]){
            continue;
        }
        if(val[i]>0){
            add(S,i,val[i]);
            add(i,S,0);
            ans+=val[i];
        }
        else{
            add(i,T,-val[i]);
            add(T,i,0);
        }
        for(auto x:out[i]){
            if(!vis[x]){
                continue;
            }
            add(x,i,inf);
            add(i,x,0);
        }
    }
    write_endl(ans-dinic(S,T));
    return 0;
}

[SDOI2013]费用流

题面

别看到题目叫作费用流,就一股脑写费用流了。注意到题目中有句话,Bob在分配单位花费之前,已经知道Alice所给出的最大流方案。那么Bob的赋权方法就是确定的,将所有权值赋给这个最大流方案中流量最大的边,那么题目要求的东西就变成了求最大流并求一个最大流方案,使得流量最大的边流量最小。

求最大值最小,二分最大边流量 \(x\),所有边的流量对 \(x\)\(\min\)。看这样的图上的最大流是否是原图上的最大流,是则说明这个最大流量是合法,反之亦然。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pdi pair<double,int>
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define eps 1e-9
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
            }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=110,M=1e3+10,inf=1e9;
int head[N],tot=1,n,m,S,T,val;
double max_flow;
struct edge{
    int v,nxt;
    double W,w;
}e[M<<1];
int dep[N],cur[N];
bool bfs(int S,int T){
    for(int i=1;i<=n;i++){
        dep[i]=0;
    }
    dep[S]=1;
    queue<int>q;
    q.push(S);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].v;
            double w=e[i].w;
            if(!dep[v]&&w>eps){
                dep[v]=dep[u]+1;
                if(v==T){
                    return 1;
                }
                q.push(v);
            }
        }
    }
    return 0;
}
double dfs(int u,double flow,int T){
    if(u==T){
        return flow;
    }
    double s=0;
    for(int i=cur[u];i;i=e[i].nxt){
        cur[u]=i;
        int v=e[i].v;
        double w=e[i].w;
        if(dep[v]==dep[u]+1&&w>eps){
            double res=dfs(v,min(flow,w),T);
            e[i].w-=res;
            e[i^1].w+=res;
            flow-=res;
            s+=res;
        }
        if(flow<eps){
            break;
        }
    }
    if(!s){
        dep[u]=0;
    }
    return s;
}
double dinic(int S,int T){
    double flow=0;
    while(bfs(S,T)){
        for(int i=1;i<=n;i++){
            cur[i]=head[i];
        }
        flow+=dfs(S,inf,T);
    }
    return flow;
}
void add(int u,int v,int w){
    e[++tot].v=v;
    e[tot].W=e[tot].w=w;
    e[tot].nxt=head[u];
    head[u]=tot;
}
void add_e(int u,int v,int w){
    add(u,v,w);
    add(v,u,0);
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    read(n),read(m),read(val);
    for(int i=1,u,v,w;i<=m;i++){
        read(u),read(v),read(w);
        add_e(u,v,w);
    }
    S=1,T=n;
    max_flow=dinic(S,T);
    write_endl((int)max_flow);
    ll l=0,r=5e9,ans=0;
    while(l<=r){
        ll mid=(l+r)>>1;
        for(int i=1;i<=tot;i++){
            e[i].w=min(e[i].W,1.0*mid/100000.0);
        }
        double flow=dinic(S,T);
        if(fabs(flow-max_flow)<eps){
            r=mid-1;
            ans=mid;
        }
        else{
            l=mid+1;
        }
    }
    printf("%.6lf\n",ans/100000.0*val);
    return 0;
}

Special Edges

题面

可以发现本题有一个很明显的特点,\(k\) 很小,所以我们可以考虑枚举每条关键边的割与否。现在我们的问题变成了,给定一个集合 \(S\) 表示 \(k\) 条边是否属于割集,求除了这 \(k\) 条边的外的边的最小割。

将集合大小变小 \(1\) 相当于在原图中新添一条不可被割的边,再跑一遍网络流就可以求出这条边对残量网络的影响。这条边边权可以改为 \(inf\),也可以为了方便运算,改为边权上限 \(25\)。至于两者等价的原因,因为 \(25\) 是边权上限,所以流量更大的话,肯定不如直接割掉这条边,对最终答案影响是相同的。

选出这条不可被割的边可以在状压后使用 lowbit 得到最低位的 \(1\) 的位置,该位置就是要删去的边。

因为本题出题人卡常,所以只建议第一次网络流用 dinic,后面的全部用 FF。

点击查看代码
#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=1e4+10,M=2e4+10,MX=(1<<10)+10,inf=1e9;
int n,m,k,q,s,t,cur[N],tot=1,head[N],all;
int que[N],l,r,dep[N];
int pre[N],pre_to[N],f[N],Lg[N],L[N],val[N],g[N];
struct edge{
    int v,w,nxt;
}e[M],G[M][MX];
void add(int u,int v,int w){
    e[++tot].v=v;
    e[tot].w=w;
    e[tot].nxt=head[u];
    head[u]=tot;
}
void add_e(int u,int v,int w){
    add(u,v,w);
    add(v,u,0);
}
bool bfs(){
    for(int i=1;i<=n;i++){
        que[i]=dep[i]=0;
    }
    l=r=0;
    que[++r]=s;
    dep[s]=1;
    while(l<r){
        int u=que[++l];
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].v,w=e[i].w;
            if(!dep[v]&&w){
                dep[v]=dep[u]+1;
                if(v==t){
                    return 1;
                }
                que[++r]=v;
            }
        }
    }
    return 0;
}
int dfs(int u,int flow){
    if(u==t){
        return flow;
    }
    int ans=0;
    for(int i=cur[u];i;i=e[i].nxt){
        cur[u]=i;
        int v=e[i].v,w=e[i].w;
        if(dep[v]==dep[u]+1&&w){
            int res=dfs(v,min(flow,w));
            e[i].w-=res;
            e[i^1].w+=res;
            ans+=res;
            flow-=res;
        }
        if(!flow){
            break;
        }
    }
    return ans;
}
int dinic(){
    int ans=0;
    while(bfs()){
        for(int i=1;i<=n;i++){
            cur[i]=head[i];
        }
        ans+=dfs(s,inf);
    }
    return ans;
}
int BFS(){
    for(int i=1;i<=n;i++){
        dep[i]=que[i]=0;
    }
    l=r=0;
    dep[s]=1;
    que[++r]=s;
    while(l<r){
        int u=que[++l];
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].v,w=e[i].w;
            if(!dep[v]&&w){
                dep[v]=dep[u]+1;
                pre[v]=u;
                pre_to[v]=i;
                que[++r]=v;
            }
            if(dep[t]){
                break;
            }
        }
        if(dep[t]){
            break;
        }
    }
    if(!dep[t]){
        return 0;
    }
    int ans=25,u;
    u=t;
    while(u!=s){
        ans=min(ans,e[pre_to[u]].w);
        u=pre[u];
    }
    u=t;
    while(u!=s){
        e[pre_to[u]].w-=ans;
        e[pre_to[u]^1].w+=ans;
        u=pre[u];
    }
    return ans;
}
int FF(){
    int ans=0,tmp;
    while(tmp=BFS()){
        ans+=tmp;
    }
    return ans;
}
int lowbit(int x){
    return x&(-x);
}
void solve(){
    read(n),read(m),read(k),read(q);
    all=(1<<k)-1;
    s=1,t=n;
    for(int i=1,u,v,w;i<=m;i++){
        read(u),read(v),read(w);
        add_e(u,v,w);
    }
    f[0]=dinic();
    for(int i=2;i<=tot;i++){
        G[i][0]=e[i];
    }
    Lg[1]=1;
    for(int i=2;i<=all;i<<=1){
        Lg[i]=Lg[i>>1]+1;
    }
    for(int i=1;i<=all;i++){
        L[i]=lowbit(i);
        for(int j=2;j<=tot;j++){
            e[j]=G[j][i^L[i]];
        }
        e[Lg[L[i]]<<1].w=25;
        f[i]=FF()+f[i^L[i]];
        for(int j=2;j<=tot;j++){
            G[j][i]=e[j];
        }
    }
    while(q--){
        for(int i=1;i<=k;i++){
            read(val[i]);
        }
        int ans=inf;
        for(int i=1;i<=all;i++){
            g[i]=g[i^L[i]]+val[Lg[L[i]]];
        }
        for(int i=0;i<=all;i++){
            ans=min(ans,f[i]+g[i^all]);
        }
        write_endl(ans);
    }
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t=1;
    while(t--){
        solve();
    }
    return 0;
}

无限之环

题面

看到水管的形状,我们发现和插头 dp 中插头的样子很像,容易想到插头dp。但是有一个需要注意的是本题中直线型是不能转的,但在插头 dp 中直线型和别的东西是没有区别的,这引导我们去思考其它的做法。

先放弃观察不合法情况,考虑以下合法情况是怎么回事。对于一个合法状态,满足要求则需要水管两两配对,将一个点拆成 \(5\) 个点,分别表示中间和四个方向的水管。两个水管匹配,则在两个水管之间连条边,最后是求最大匹配是否为 \(\frac{sum}{2}\),其中 \(sum\) 表示水管总数。这一般用什么?网络流!!!
于是我们顺着这个思路想下去,根据一个比较经典的 trick,将网格黑白染色,从拆出来的点中代表相邻水管的两边连一条费用为 \(0\),流量为 \(1\) 的边。如果存在一个水管,则从中间连向对应方向,连一条费用为 \(0\),流量为 \(1\) 的边。
对于旋转,分为 \(5\) 种情况,单向,拐角,直线,T形,十字。其中直线和十字不能旋转,不考虑。先只考虑白点,黑点和白点相反建边即可。单向水管旋转,水管会换一个方向,从起始方向向转到的方向连一条费用为旋转次数,流量为 \(1\) 的边。拐角旋转,水管会变到对向的位置,从两个位置各自向对向的点连边,费用为 \(1\),流量为 \(1\),在这里,两条边各走一遍,就是相当于旋转两次。T形旋转会有一个点旋转到空白点,从转来的方向向空白点方向连一条费用为旋转次数,流量为 \(1\) 的边。当最大流等于 \(\frac{sum}{2}\) 时,为有解,解为最小费用。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=1e4+10,M=2e5+10,inf=1e9;
int head[N],n,m,tot=1,s,t,maxflow,mincost,sum;
struct edge{
    int v,flow,cost,nxt;
}e[M<<1];
void add(int u,int v,int flow,int cost){
    e[++tot].v=v;
    e[tot].flow=flow;
    e[tot].cost=cost;
    e[tot].nxt=head[u];
    head[u]=tot;
}
void add_e(int u,int v,int flow,int cost){
    add(u,v,flow,cost);
    add(v,u,0,-cost);
}
namespace MCMF{
    int d[N],flow[N],b[N],pre[N],pre_to[N];
    int spfa(){
        for(int i=1;i<=t;i++){
            d[i]=flow[i]=inf;
            b[i]=pre[i]=pre_to[i]=0;
        }
        queue<int>q;
        q.push(s);
        b[s]=1;
        d[s]=0;
        pre[t]=0;
        while(!q.empty()){
            int u=q.front();
            q.pop();
            b[u]=0;
            for(int i=head[u];i;i=e[i].nxt){
                int v=e[i].v,w=e[i].flow,c=e[i].cost;
                if(w>0&&d[v]>d[u]+c){
                    d[v]=d[u]+c;
                    pre[v]=u;
                    pre_to[v]=i;
                    flow[v]=min(flow[u],w);
                    if(!b[v]){
                        b[v]=1;
                        q.push(v);
                    }
                }
            }
        }
        return pre[t];
    }
    void solve(){
        while(spfa()){
            int u=t;
            maxflow+=flow[t];
            mincost+=flow[t]*d[t];
            while(u!=s){
                e[pre_to[u]].flow-=flow[t];
                e[pre_to[u]^1].flow+=flow[t];
                u=pre[u];
            }
        }
    }
}
int id(int x,int y,int opt){
    return (x-1)*m+y+opt*n*m;
}
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
void buildA(int x,int y,int opt,int type){
    if(type){
        add_e(id(x,y,opt),id(x,y,(opt+3)%4),1,1);
        add_e(id(x,y,opt),id(x,y,(opt+1)%4),1,1);
        add_e(id(x,y,opt),id(x,y,(opt+2)%4),1,2);
    }
    else{
        add_e(id(x,y,(opt+3)%4),id(x,y,opt),1,1);
        add_e(id(x,y,(opt+1)%4),id(x,y,opt),1,1);
        add_e(id(x,y,(opt+2)%4),id(x,y,opt),1,2);
    }
}
void buildB(int x,int y,int opt,int type){
    return;
}
void buildC(int x,int y,int opt,int type){
    if(type){
        add_e(id(x,y,opt),id(x,y,(opt+2)%4),1,1);
        add_e(id(x,y,(opt+1)%4),id(x,y,(opt+3)%4),1,1);
    }
    else{
        add_e(id(x,y,(opt+2)%4),id(x,y,opt),1,1);
        add_e(id(x,y,(opt+3)%4),id(x,y,(opt+1)%4),1,1);
    }
}
void buildD(int x,int y,int opt,int type){
    if(type){
        add_e(id(x,y,(opt+3)%4),id(x,y,opt),1,1);
        add_e(id(x,y,(opt+1)%4),id(x,y,opt),1,1);
        add_e(id(x,y,(opt+2)%4),id(x,y,opt),1,2);
    }
    else{
        add_e(id(x,y,opt),id(x,y,(opt+3)%4),1,1);
        add_e(id(x,y,opt),id(x,y,(opt+1)%4),1,1);
        add_e(id(x,y,opt),id(x,y,(opt+2)%4),1,2);
    }
}
void buildE(int x,int y,int opt,int type){
    return;
}
void solve(){
    read(n),read(m);
    s=n*m*5+1,t=n*m*5+2;
    int s1=0,s2=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            int opt;
            read(opt);
            vector<int>pos;
            if((i+j)%2){
                add_e(s,id(i,j,4),inf,0);
                for(int k=0;k<4;k++){
                    if(opt>>k&1){
                        add_e(id(i,j,4),id(i,j,k),1,0);
                        pos.pb(k);
                        sum++;
                        s1++;
                    }
                    int x=i+dx[k],y=j+dy[k];
                    if(x<=0||y<=0||x>n||y>m){
                        continue;
                    }
                    add_e(id(i,j,k),id(x,y,(k+2)%4),1,0);
                }
            }
            else{
                add_e(id(i,j,4),t,inf,0);
                for(int k=0;k<4;k++){
                    if(opt>>k&1){
                        add_e(id(i,j,k),id(i,j,4),1,0);
                        pos.pb(k);
                        s2++;
                    }
                }
            }
            if(pos.size()==1){
                buildA(i,j,pos[0],(i+j)%2);
            }
            else if(pos.size()==2&&abs(pos[1]-pos[0])==2){
                buildB(i,j,pos[0],(i+j)%2);
            }
            else if(pos.size()==2){
                if(opt==3){
                    buildC(i,j,0,(i+j)%2);
                }
                else if(opt==6){
                    buildC(i,j,1,(i+j)%2);
                }
                else if(opt==12){
                    buildC(i,j,2,(i+j)%2);
                }
                else{
                    buildC(i,j,3,(i+j)%2);
                }
            }
            else if(pos.size()==3){
                if(pos[0]!=0){
                    buildD(i,j,0,(i+j)%2);
                }
                else if(pos[1]!=1){
                    buildD(i,j,1,(i+j)%2);
                }
                else if(pos[2]!=2){
                    buildD(i,j,2,(i+j)%2);
                }
                else if(pos[3]!=3){
                    buildD(i,j,3,(i+j)%2);
                }
            }
            else{
                buildE(i,j,0,(i+j)%2);
            }
        }
    }
    if(s1!=s2){
        puts("-1");
        return;
    }
    MCMF::solve();
    if(maxflow!=sum){
        puts("-1");
    }
    else{
        write_endl(mincost);
    }
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t=1;
    while(t--){
        solve();
    }
    return 0;
}

Incorrect Flow

题面

分类讨论,分为 \(f\le c\)\(f>c\) 两类。

下文中所有的流量上限全部为减少或增大流量的上限。

对于 \(f\le c\) 的边,存在三种可行的操作:

  1. \(f\) 减小,即退流,从 \(v\)\(u\) 连一条流量为 \(f\),费用为 \(1\) 的边。
  2. \(f\) 增大且不超过 \(c\),即增大流,从 \(u\)\(v\) 连一条流量为 \(c-f\),费用为 \(1\) 的边。
  3. \(f\) 增大且超过 \(c\),此时要同时增大流和容量,从 \(u\)\(v\) 连一条流量为 \(+\infty\),费用为 \(2\) 的边。

对于 \(f>c\) 的边,同样存在 \(3\) 种可行操作:

  1. \(f\) 减小且小于 \(c\),退流操作,从 \(v\)\(u\) 连一条流量为 \(c\) ,费用为 \(1\) 的边,然后还要额外提供 \(f-c\) 的费用。
  2. \(f\) 减小且大于 \(c\),此时它不仅退了流,还增大了容量上限,显然无论如何,费用都为 \(f-c\),但这不好统计,于是我们将 \(f-c\) 提出来,再从 \(v\)\(u\) 连一条流量为 \(f-c\),费用为 \(0\) 的边。
  3. \(f\) 增大,除了 \(f-c\) 的费用,流每增大 \(1\),费用都要增加 \(2\),因此从 \(u\)\(v\) 连一条流量为 \(+\infty\) 费用为 \(2\) 的边。

现在问题来了,原图中的 \(f\) 怎么办呢,考虑将图转化为上下界网络,对于原来的每条边,连一条从 \(u\)\(v\) 上下界均为 \(f\),费用为 \(0\) 的边,于是我们先将这 \(f\) 流完,假定如果一个点流入比流出多 \(d_i\),从超级源点向该点连一条流量为 \(d_i\),费用为 \(0\) 的边,反之则从该点向超级汇点连一条流量为 \(d_i\),费用为 \(0\) 的边。

最后跑一遍费用流即可。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=110,M=2e3+10,inf=1e9;
int n,m,head[N],tot=1,Flow[N],s,t,maxflow,mincost;
struct edge{
    int v,flow,cost,nxt;
}e[M<<1];
void add(int u,int v,int flow,int cost){
    e[++tot].v=v;
    e[tot].flow=flow;
    e[tot].cost=cost;
    e[tot].nxt=head[u];
    head[u]=tot;
}
void add_e(int u,int v,int flow,int cost){
    add(u,v,flow,cost);
    add(v,u,0,-cost);
}
namespace MCMF{
    int d[N],flow[N],b[N],pre[N],pre_to[N];
    int spfa(){
        for(int i=1;i<=t;i++){
            d[i]=flow[i]=inf;
            b[i]=pre[i]=pre_to[i]=0;
        }
        queue<int>q;
        q.push(s);
        b[s]=1;
        d[s]=0;
        pre[t]=0;
        while(!q.empty()){
            int u=q.front();
            q.pop();
            b[u]=0;
            for(int i=head[u];i;i=e[i].nxt){
                int v=e[i].v,w=e[i].flow,c=e[i].cost;
                if(w>0&&d[v]>d[u]+c){
                    d[v]=d[u]+c;
                    pre[v]=u;
                    pre_to[v]=i;
                    flow[v]=min(flow[u],w);
                    if(!b[v]){
                        b[v]=1;
                        q.push(v);
                    }
                }
            }
        }
        return pre[t];
    }
    void solve(){
        while(spfa()){
            int u=t;
            maxflow+=flow[t];
            mincost+=flow[t]*d[t];
            while(u!=s){
                e[pre_to[u]].flow-=flow[t];
                e[pre_to[u]^1].flow+=flow[t];
                u=pre[u];
            }
        }
    }
}
void solve(){
    read(n),read(m);
    s=n+1,t=n+2;
    int S=0;
    for(int i=1;i<=m;i++){
        int u,v,flow,sum;
        read(u),read(v),read(flow),read(sum);
        if(flow>=sum){
            add_e(v,u,sum,1);
            add_e(u,v,flow-sum,1);
        }
        else{
            S+=sum-flow;
            add_e(v,u,flow,1);
            add_e(v,u,sum-flow,0);
        }
        Flow[u]+=sum;
        Flow[v]-=sum;
        add_e(u,v,inf,2);
    }
    add_e(n,1,inf,0);
    for(int i=1;i<=n;i++){
        if(Flow[i]>0){
            add_e(i,t,Flow[i],0);
        }
        else{
            add_e(s,i,-Flow[i],0);
        }
    }
    MCMF::solve();
    write_endl(S+mincost);
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t=1;
    while(t--){
        solve();
    }
    return 0;
}

Showing Off

题面

从一个点向它到达的点连边,可以发现这是一个基环树森林。

先考虑可以不在环上的点要满足什么条件,因为权值图均为正整数,所以它必然是从一个小于它的点移过来的,所以它的周围四格必然要存在一个小于它的数。换过来说就是如果一个点周围四格的数均大于等于它,那么它必然在环上。

将原图黑白染色,得到所有的环均为偶环。既然所有环均为偶环,所以我们可以将所有的环全部拆成大小为 \(2\) 的环,这是等效的。所以我们把一个环转化为了一个匹配。对于所有的黑点,连一条从 \(s\) 到它,流量为 \([x_{i,j},1]\) 的边,其中 \(x_{i,j}\) 表示 \((i,j)\) 是否一定在环上,白点同样连一条从它到 \(t\) 的边,流量为 \([x_{i,j},1]\) 的边,对于相邻的点 \((i,j),(x,y)\),如果 \(a_{i,j}=a_{x,y}\) 从黑点向白点连一条流量为 \([0,1]\) 的边,最后跑可行流,如果可行,则将匹配的两个点 \((i,j),(x,y)\) 权值变为 \(1,a_{i,j}-1\)。对于不在环上的点,权值为 \(a_{i,j}-a_{x,y}\)\(a_{x,y}<a_{i,j}\)

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=1e5+10,inf=1e9;
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
char opt[4]={'D','R','U','L'};
int n,m,head[N],tot=1,s,t,S,T;
int a[N],b[N],c[N],vis[N];
struct edge{
    int v,flow,nxt;
}e[N<<4];
int id(int x,int y){
    return (x-1)*m+y;
}
void add(int u,int v,int w){
    e[++tot].v=v;
    e[tot].flow=w;
    e[tot].nxt=head[u];
    head[u]=tot;
}
void add_e(int u,int v,int w){
    add(u,v,w);
    add(v,u,0);
}
void Add(int u,int v,int opt,int type){
    if(!opt){
        add_e(u,v,type);
    }
    else{
        add_e(S,v,1);
        add_e(u,T,1);
    }
}
namespace Max_Flow{
    int dep[N],cur[N];
    bool bfs(){
        for(int i=1;i<=T;i++){
            dep[i]=0;
            cur[i]=head[i];
        }
        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].nxt){
                int v=e[i].v,w=e[i].flow;
                if(w&&!dep[v]){
                    dep[v]=dep[u]+1;
                    q.push(v);
                    if(v==T){
                        return 1;
                    }
                }
            }
        }
        return 0;
    }
    int dfs(int u,int flow){
        if(u==T){
            return flow;
        }
        int s=0;
        for(int i=cur[u];i;i=e[i].nxt){
            cur[u]=i;
            int v=e[i].v,w=e[i].flow;
            if(w&&dep[v]==dep[u]+1){
                int res=dfs(v,min(flow,w));
                e[i].flow-=res;
                e[i^1].flow+=res;
                flow-=res;
                s+=res;
            }
            if(!flow){
                break;
            }
        }
        if(!s){
            dep[u]=0;
        }
        return s;
    }
    int dinic(){
        int ans=0;
        while(bfs()){
            for(int i=1;i<=T;i++){
                cur[i]=head[i];
            }
            ans+=dfs(S,inf);
        }
        return ans;
    }
}
void solve(){
    read(n),read(m);
    s=n*m+1;
    t=n*m+2;
    S=n*m+3;
    T=n*m+4;
    for(int i=1;i<=T;i++){
        head[i]=0;
        b[i]=c[i]=vis[i]=0;
    }
    tot=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            read(a[id(i,j)]);
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            for(int k=0;k<4;k++){
                int x=i+dx[k],y=j+dy[k];
                if(x<=0||y<=0||x>n||y>m){
                    continue;
                }
                vis[id(i,j)]|=(a[id(i,j)]>a[id(x,y)]);
            }
            vis[id(i,j)]^=1;
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if((i+j)&1){
                Add(s,id(i,j),vis[id(i,j)],1);
                for(int way=0;way<4;way++){
                    if(i+dx[way]<1||i+dx[way]>n||j+dy[way]<1||j+dy[way]>m){
                        continue;
                    }
                    if(a[id(i,j)]==a[id(i+dx[way],j+dy[way])]){
                        add_e(id(i,j),id(i+dx[way],j+dy[way]),1);
                    }
                }
            }
            else{
                Add(id(i,j),t,vis[id(i,j)],1);
            }
        }
    }
    add(t,s,inf);
    int sum=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            sum+=vis[id(i,j)];
        }
    }
    if(Max_Flow::dinic()!=sum){
        puts("NO");
        return;
    }
    puts("YES");
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if((i+j)&1){
                for(int k=head[id(i,j)];k;k=e[k].nxt){
                    int v=e[k].v;
                    if(v!=s&&e[k^1].flow){
                        for(int way=0;way<4;way++){
                            int x=i+dx[way],y=j+dy[way];
                            if(x<1||y<1||x>n||y>m){
                                continue;
                            }
                            if(id(x,y)==v){
                                b[id(x,y)]=a[id(i,j)]-1;
                                b[id(i,j)]=1;
                                c[id(x,y)]=(way+2)%4;
                                c[id(i,j)]=way;
                            }
                        }
                    }
                }
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(!b[id(i,j)]){
                for(int way=0;way<4;way++){
                    int x=i+dx[way],y=j+dy[way];
                    if(x<1||y<1||x>n||y>m){
                        continue;
                    }
                    if(a[id(x,y)]<a[id(i,j)]){
                        c[id(i,j)]=way;
                        b[id(i,j)]=a[id(i,j)]-a[id(x,y)];
                    }
                }
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            write_space(b[id(i,j)]);
        }
        putchar('\n');
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            putchar(opt[c[id(i,j)]]);
            putchar(' ');
        }
        putchar('\n');
    }
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t;
    read(t);
    while(t--){
        solve();
    }
    return 0;
}
posted @ 2023-05-25 21:48  luo_shen  阅读(36)  评论(1)    收藏  举报