【网络流+线段树】[CQBZOJ3065]生死游戏((A+B)^2 Problem)

题目

题目描述
有些邪恶富人们喜欢玩生死游戏。简单的说就是找一些穷人,让他们进行相互厮杀。富人们则在旁观看并下注。
今天的游戏跟以往有些不同。参与游戏的穷人排成了一个n*m的矩阵,你的任务是组织游戏并决定这些人的生死。
如果第i行,第j列的人幸存了下来,你将获得Wij块钱,否则你将得到Bij块钱。
同时,富人们会提出一些奇怪的要求。他们每个人都会指定一个子矩阵,然后说:如果这个子矩阵的所有穷人都死了(或者都幸存下来了),你将得到S块钱。
你并不关心这些穷人的生死,你只希望你得到的钱尽可能多。
请注意,这里对杀人没有限制,你可以杀掉所有人,也可以一个也不杀。
输入
第一行,三个空格间隔的整数n,m,r ,分别表示矩阵的行数、列数和富人们提出的要求的数量。
接下来是n*m个整数,表示矩阵B。
接下来是n*m个整数,表示矩阵W。(0<=Bij,Wij<=100)
接下来r行,每行描述一个要求:
每个要求有6个空格间隔的整数构成:R1,C1,R2,C2,T,S。它表示一个子矩阵的左上角(R1,C1)和右下角(R2,C2)的坐标。T=1表示矩阵中的所有人必须死,T=0表示矩阵中所有人都必须活下来。S表示如果你满足了这个要求,你将得到的钱数。(0<=S<=10000)请注意,这里对杀人没有限制,你可以杀掉所有人,也可以一个也不杀。
输出
一行,一个整数,表示所求答案。
样例1
输入
2 2 3
34 44
63 30
1 9
53 57
1 2 2 2 1 2843
1 1 2 1 0 2169
2 1 2 1 1 6980

输出
9994

样例2
输入
2 2 3
50 93
65 70
52 28
91 25
1 1 2 1 0 9862
2 1 2 1 1 1876
2 2 2 2 0 4190

输出
14313

提示
对于30%的数据 1<=n,m<=10 0<=r=1000

对于50%的数据 1<=n,m<=30 0<=r<=10000

对于100%的数据 1<=n,m<=50 0<=r<=50000

分析

这道题的选择之间有关系,很容易想到最小割。

ans=i=1nj=1mbij+wiji,ji,jwi,ji,ji,jbi,jiisi

我们从S到每个点连容量为 bi 的边,从每个点到T连容量为 wi 的边,当点在S时,就代表这个人死了,在T则活着。
然后考虑富人的要求,对于每个要求我们新增一个点。
对于第 i 个要求:

  • 如果要求所有人都活着,那么当这个点在S时,就会付出 si 的代价。就从这个点到T连一条容量为 si 的边,再从矩阵内所有的点向这个点连一条容量为 + 的边。
  • 如果要求所有人都死,那么当这个点在T时,就会付出 si 的代价。就从S到这个点连一条容量为 si 的边,再从这个点到矩阵内所有的点连一条容量为 + 的边。

    显然边十分多,但是连接的点往往是在一个二维区间内的所有点,可以效仿A+B Problem,用二维线段树优化这道题。

这道题,其实就是 (A+B)2Problem

代码

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define MAXN 50
#define MAXR 50000
#define INF 0x7fffffff
using namespace std;
queue<int>q;
struct node{
    int v,cap;
    node *back,*next;
}*adj[MAXN*MAXN*20+MAXR+10],edge[10000000+10],*ecnt=edge;
int root[MAXN*4+10],ls[MAXN*MAXN*4*4+10],rs[MAXN*MAXN*4*4+10],tot,m,S,T,n,r,ans,c,dist[MAXN*MAXN*20+MAXR+10],vd[MAXN*MAXN*20+MAXR+10],flow,id[MAXN*MAXN*4*4+10][2],tcnt;
void addedge(int u,int v,int cap){
    node *p=++ecnt;
    p->v=v;
    p->cap=cap;
    p->next=adj[u];
    adj[u]=p;
    p=p->back=++ecnt;
    p->v=u;
    p->cap=0;
    p->next=adj[v];
    adj[v]=p;
    p->back=ecnt-1;
}
void build2(int &i,int l,int r,int ll,int rr){
    i=++tcnt;
    id[i][0]=++tot,id[i][1]=++tot;
    if(l==r){
        for(int j=ll;j<=rr;j++){
            addedge(id[i][1],(j-1)*m+l,INF);
            addedge((j-1)*m+l,id[i][0],INF);
        }
        return;
    }
    int mid=(l+r)>>1;
    build2(ls[i],l,mid,ll,rr);
    build2(rs[i],mid+1,r,ll,rr);
    addedge(id[i][1],id[ls[i]][1],INF);
    addedge(id[i][1],id[rs[i]][1],INF);
    addedge(id[ls[i]][0],id[i][0],INF);
    addedge(id[rs[i]][0],id[i][0],INF);
}
void build1(int i,int l,int r){
    build2(root[i],1,m,l,r);
    if(l==r)
        return;
    int mid=(l+r)>>1;
    build1(i<<1,l,mid);
    build1((i<<1)|1,mid+1,r);
}
void link2(int i,int l,int r,int ll,int rr,bool f,int c){
    if(ll<=l&&rr>=r){
        if(f)
            addedge(tot,id[i][1],c);
        else 
            addedge(id[i][0],tot,c);
        return;
    }
    if(l>rr||r<ll)
        return;
    int mid=(l+r)>>1;
    link2(ls[i],l,mid,ll,rr,f,c);
    link2(rs[i],mid+1,r,ll,rr,f,c);
}
void link1(int i,int l,int r,int ll,int rr,int wl,int wr,bool f,int c){
    if(ll<=l&&rr>=r){
        link2(root[i],1,m,wl,wr,f,c);
        return;
    }
    if(l>rr||r<ll)
        return;
    int mid=(l+r)>>1;
    link1(i<<1,l,mid,ll,rr,wl,wr,f,c);
    link1((i<<1)|1,mid+1,r,ll,rr,wl,wr,f,c);
}
void Read(int &x){
    char c;
    while(c=getchar(),c!=EOF)
        if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            return;
        }
}
void read(){
    Read(n),Read(m),Read(r);
    int i,j,b,w;
    S=n*m+1,tot=T=S+1;
    build1(1,1,n);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++){
            Read(b);
            addedge(S,(i-1)*m+j,b);
            ans+=b;
        }
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++){
            Read(w);
            addedge((i-1)*m+j,T,w);
            ans+=w;
        }
    int h1,h2,s1,s2,t,s;
    for(i=1;i<=r;i++){
        Read(h1),Read(s1),Read(h2),Read(s2),Read(t),Read(s);
        ans+=s;
        if(t){
            addedge(S,++tot,s);
            link1(1,1,n,h1,h2,s1,s2,1,s);
        }
        else{
            addedge(++tot,T,s);
            link1(1,1,n,h1,h2,s1,s2,0,s);
        }

    }
}
void bfs(){
    q.push(T);
    int u;
    while(!q.empty()){
        u=q.front();
        q.pop();
        for(node *p=adj[u];p;p=p->next){
            if(p->back->cap&&!dist[p->v]){
                dist[p->v]=dist[u]+1;
                q.push(p->v);
            }
        }
    }
    dist[T]=0;
}
int dfs(int u,int augu){
    if(u==T)
        return augu;
    int augv=0,v,delta,mind=tot-1;
    for(node *p=adj[u];p;p=p->next)
        if(p->cap){
            v=p->v;
            if(dist[u]==dist[v]+1){
                delta=min(augu-augv,p->cap);
                delta=dfs(v,delta);
                augv+=delta;
                p->cap-=delta;
                p->back->cap+=delta;
                if(augu==augv||dist[S]>=tot)
                    return augv;
            }
            mind=min(dist[v],mind);
        }
    if(!augv){
        if(!--vd[dist[u]])
            dist[S]=tot;
        vd[dist[u]=mind+1]++;
    }
    return augv;
}
void sap(){
    bfs();
    for(int i=1;i<=tot;i++){
        if(!dist[i]){
            dist[i]=tot;
            continue;
        }
        vd[dist[i]]++;
    }
    dist[T]=0,vd[0]++;
    while(dist[S]<tot)
        flow+=dfs(S,INF);
}
int main()
{
    read();
    sap();
    printf("%d\n",ans-flow);
}

你可能感兴趣的:(数据结构,C++,线段树,网络流,algortithm)