THUSC2017 座位

在这里插入图片描述
题解

把每个位置上的人都看做是一个节点,然后如果我们能把这种换位置的关系表示出来,我们就可以把每个点拆成两个x,一个从S流进1,一个流出1到T
那么这么表示出这种关系呢
首先对于一个环上的移动,我们可以把相邻的两个点之间连一条费用1的双向边
那么桌子之间的关系呢
我们可以将每个桌子再创一个新点,这个点向这个桌子上的每个位置连一条边,然后呢,我们构造两*m个线段树,表示这个位置上的,往左/右,来辅助建图(为什么必须分开呢?因为在建边的时候的求值是跟左右相关的)
然后就这样了。
这个费用流最好是用原始对偶算法

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define lch x<<1
#define rch x<<1|1
using namespace std;
typedef long long ll;
const int N=60005,M=600005;
const int INF=0x3f3f3f3f;

struct node{
    int u,v,cost,cap,nxt;
}edge[M];
int head[N],mcnt=1;
int cur[N];
void add_edge(int u,int v,int cap,int cost){
    mcnt++;
    edge[mcnt].u=u;
    edge[mcnt].v=v;
    edge[mcnt].cap=cap;
    edge[mcnt].cost=cost;
    edge[mcnt].nxt=head[u];
    head[u]=mcnt;
}
void add(int u,int v,int cap,int cost){
    add_edge(u,v,cap,cost);
    add_edge(v,u,0,-cost);
}
queue<int>q;
int dist[N];
bool inqueue[N],vis[N];
int ans;
int S,T;
int ncnt;
bool spfa(){
    memset(dist,0x3f,sizeof dist);
    memcpy(cur,head,sizeof cur);
    dist[T]=0;
    q.push(T);
    inqueue[T]=true;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        inqueue[u]=0;
        for(int i=head[u];i;i=edge[i].nxt){
            int v=edge[i].v,w=edge[i].cost;
            if(edge[i^1].cap){
                if(dist[v]>dist[u]-w){
                    dist[v]=dist[u]-w;
                    if(!inqueue[v]){
                        inqueue[v]=1;
                        q.push(v);
                    }
                }
            }
        }
    }
    return dist[S]<INF;
}
int dfs(int u,int C){
    if(u==T)
        return C;
    vis[u]=true;
    int flow=0;
    for(int &i=cur[u];i;i=edge[i].nxt){
        int v=edge[i].v,w=edge[i].cost,c=edge[i].cap;
        if(c&&!vis[v]&&dist[v]==dist[u]-w){
            int f=dfs(v,min(C,c));
            edge[i].cap-=f;
            edge[i^1].cap+=f;
            C-=f;
            flow+=f;
            ans+=f*w;
            if(!C)
                break;
        }
    }
    vis[u]=false;
    return flow;
}
int Mincost_maxflow(){
    int flow=0;
    while(spfa()){
        flow+=dfs(S,INF);
    }
    return flow;
}

int num[305][15];
struct Segtree{
    int id[N];
    void Build(int x,int l,int r,int f,int q){
        id[x]=++ncnt;
        if(l==r){
            add(ncnt,num[l][q],INF,0);
            return ;
        }
        int mid=(l+r)>>1;
        Build(lch,l,mid,f,q);
        Build(rch,mid+1,r,f,q);
        if(f)
            add(id[x],id[lch],INF,2*(r-mid)),
            add(id[x],id[rch],INF,0);
        else
            add(id[x],id[lch],INF,0),
            add(id[x],id[rch],INF,2*(mid-l+1));
    }
    void Query(int x,int l,int r,int pl,int pr,int f,int z,int o){
        if(pl<=l&&r<=pr){
            if(f)
                add(z,id[x],1,2*(o-r));
            else
                add(z,id[x],1,2*(l-o));
            return ;
        }
        int mid=(l+r)>>1;
        if(pl<=mid)
            Query(lch,l,mid,pl,pr,f,z,o);
        if(pr>mid)
            Query(rch,mid+1,r,pl,pr,f,z,o);
    }
}T1[11],T2[11];
int n,m;
int L[305][15],R[305][15];
int main()
{
    scanf("%d%d",&n,&m);
    S=++ncnt,T=++ncnt;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            num[i][j]=++ncnt;
            add(ncnt,T,1,0);
        }
    for(int j=1;j<=m;j++){
        T1[j].Build(1,1,n,0,j);
        T2[j].Build(1,1,n,1,j);
    }
    if(m>1)
        for(int i=1;i<=n;i++){
            for(int j=1;j<m;j++)
                add(num[i][j],num[i][j+1],INF,1),
                add(num[i][j+1],num[i][j],INF,1);
            add(num[i][m],num[i][1],INF,1),
            add(num[i][1],num[i][m],INF,1);
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            scanf("%d",&L[i][j]);
            L[i][j]++;
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            scanf("%d",&R[i][j]);
            R[i][j]++;
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            ncnt++;
            add(S,ncnt,1,0);
            if(R[i][j]<=i)
                T2[j].Query(1,1,n,L[i][j],R[i][j],1,ncnt,i);
            else if(L[i][j]>=i)
                T1[j].Query(1,1,n,L[i][j],R[i][j],0,ncnt,i);
            else{
                T2[j].Query(1,1,n,L[i][j],i,1,ncnt,i);
                T1[j].Query(1,1,n,i+1,R[i][j],0,ncnt,i);
            }
        }
    int flow=Mincost_maxflow();
    if(flow!=n*m)
        puts("no solution");
    else
        printf("%d\n",ans);
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值