【网络流+线段树】[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
分析
这道题的选择之间有关系,很容易想到最小割。
我们从S到每个点连容量为
然后考虑富人的要求,对于每个要求我们新增一个点。
对于第
- 如果要求所有人都活着,那么当这个点在S时,就会付出
si 的代价。就从这个点到T连一条容量为si 的边,再从矩阵内所有的点向这个点连一条容量为+∞ 的边。 如果要求所有人都死,那么当这个点在T时,就会付出
si 的代价。就从S到这个点连一条容量为si 的边,再从这个点到矩阵内所有的点连一条容量为+∞ 的边。显然边十分多,但是连接的点往往是在一个二维区间内的所有点,可以效仿A+B Problem,用二维线段树优化这道题。
这道题,其实就是
代码
#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);
}