【HNOI】d 最小割

  【题目大意】给定一个n*m的土地,每块可以种a或b作物,每种作物在不同的位置有不同的收成,同时,有q个子矩阵中,全部种指定的作物(a或b)会有一定的加成收成,求最大收成。  

  【数据范围】

    50% n,m<=10 q<=500

    100% n,m<=100 q<=50000

  首先我们解决小范围数据,比较容易的可以看出来这是一个最小割模型,先将ans+=value。我们只需要(source,i,value[i][0]),表示不种植a的代价,(i,sink,value[i][1])表示不种植b的代价。对于额外的加成,如果全是b作物,我们可以表示为(x,cur,inf),(cur,sink,value) x为矩阵中的所有点,这个表示我们只要矩阵中的任意一个元素没有种植b(也就是某个点割得与sink相连的边),那么我们都可以找到一条新的增广路,流量为value。

  那么我们可以发现,这种建模的边是n*m*q级别的,因为每次我们新的cur点都与矩阵中所有的点连接了,我们需要来增加图的点的数量来减少边的数量,那么我们可以用二维st表来表示每个矩阵中的点,num[i][j][p][q]表示矩阵中i,j点为左上角,长为2^p,宽为2^q的矩阵,我们将图拆为a,b两层,分别表示a,b作物的矩阵。

  因为新加入的点是为了简化之前的图的,所以我们同层的st表之间的边应该与之前连接的边的方向相同,因为我们这样做相当与把原图拆成了两部分,原图的两部分之间是互通的,所以我们也应该将两层st表之间加上双向边,如果不加这个的话,会出现由于额外价值过大导致割了连接源和汇的边而保留两个附加收成的点,这样当然是不合法的。

  反思:开始建的是正方形的st表,后来发现了这种建发的诸多不便,这样不能保证图的规模,因为条形的矩阵可以卡掉这个。然后开始的图没有两层之间双向连边,所以导致了些奇奇怪怪的问题(也不奇怪,就是上述的不合法割边)。

//By BLADEVIL

#include <cstdio>

#include <cstring>

#include <algorithm>

#define maxm 2000010

#define maxn 110

#define inf (1000000000)



using namespace std;



int n,m,query;

int source,sink,tot,l;

int key[maxn][maxn][3],num[maxn][maxn][10][10][3];

int pre[maxm],other[maxm],last[maxm],len[maxm];

int que[maxm],dis[maxm];



void connect(int x,int y,int z) {

    pre[++l]=last[x];

    last[x]=l;

    other[l]=y;

    len[l]=z;

    //printf("|%d %d %d\n",x,y,z);

}



bool bfs() {

    memset(dis,0,sizeof dis);

    que[1]=source; dis[source]=1;

    int h=0,t=1;

    while (h<t) {

        int cur=que[++h];

        for (int p=last[cur];p;p=pre[p]) {

            if (len[p]<=0) continue;

            if (!dis[other[p]]) {

                que[++t]=other[p];

                dis[other[p]]=dis[cur]+1;

                if (other[p]==sink) return true;

            }

        }

    }

    return false;

}



int dinic(int x,int flow) {

    //printf("%d %d\n",x,flow);

    if (x==sink) return flow;

    int rest=flow;

    for (int p=last[x];p;p=pre[p]) {

        if (len[p]<=0) continue;

        if (!rest) continue;

        if (dis[other[p]]!=dis[x]+1) continue;

        int tmp=dinic(other[p],min(rest,len[p]));

        len[p]-=tmp; len[p^1]+=tmp; rest-=tmp;

    }

    return flow-rest;

}



int main() {

    freopen("d.in","r",stdin); freopen("d.out","w",stdout);

    scanf("%d%d%d",&n,&m,&query);

    for (int i=1;i<=n;i++)

        for (int j=1;j<=m;j++) scanf("%d",&key[i][j][0]);

    for (int i=1;i<=n;i++)

        for (int j=1;j<=m;j++) scanf("%d",&key[i][j][1]);

    l=1;

    for (int p=0;(1<<p)<=n;p++)    

        for (int q=0;(1<<q)<=m;q++) 

            for (int i=1;i+(1<<p)-1<=n;i++)

                for (int j=1;j+(1<<q)-1<=m;j++) {

                    num[i][j][p][q][0]=++tot; num[i][j][p][q][1]=++tot;

                }

    source=++tot; sink=++tot;

    int ans=0;

    for (int i=1;i<=n;i++)

        for (int j=1;j<=m;j++) {

            connect(source,num[i][j][0][0][1],key[i][j][1]); connect(num[i][j][0][0][1],source,0);

            connect(num[i][j][0][0][0],sink,key[i][j][0]); connect(sink,num[i][j][0][0][0],0);

            ans+=key[i][j][0]+key[i][j][1];

        }

    for (int i=1;i<=n;i++)

        for (int j=1;j<=m;j++)

            connect(num[i][j][0][0][1],num[i][j][0][0][0],inf),connect(num[i][j][0][0][0],num[i][j][0][0][1],0);

    for (int p=0;(1<<p)<=n;p++)

        for (int q=0;(1<<q)<=m;q++)

            for (int i=1;i+(1<<p)-1<=n;i++)

                for (int j=1;j+(1<<q)-1<=m;j++) {

                    connect(num[i][j][p][q][0],num[i][j][p][q][1],inf);connect(num[i][j][p][q][1],num[i][j][p][q][0],0);

                    if (q) {

                        connect(num[i][j][p][q][0],num[i][j][p][q-1][0],inf); connect(num[i][j][p][q-1][0],num[i][j][p][q][0],0);

                        connect(num[i][j][p][q][0],num[i][j+(1<<(q-1))][p][q-1][0],inf); connect(num[i][j+(1<<(q-1))][p][q-1][0],num[i][j][p][q][0],0);

                        connect(num[i][j][p][q-1][1],num[i][j][p][q][1],inf); connect(num[i][j][p][q][1],num[i][j][p][q-1][1],0);

                        connect(num[i][j+(1<<(q-1))][p][q-1][1],num[i][j][p][q][1],inf); connect(num[i][j][p][q][1],num[i][j+(1<<(q-1))][p][q-1][1],0);

                    } else 

                    if (p) {

                        connect(num[i][j][p][q][0],num[i][j][p-1][q][0],inf); connect(num[i][j][p-1][q][0],num[i][j][p][q][0],0);

                        connect(num[i][j][p][q][0],num[i+(1<<(p-1))][j][p-1][q][0],inf); connect(num[i+(1<<(p-1))][j][p-1][q][0],num[i][j][p][q][0],0);

                        connect(num[i][j][p-1][q][1],num[i][j][p][q][1],inf); connect(num[i][j][p][q][1],num[i][j][p-1][q][1],0);

                        connect(num[i+(1<<(p-1))][j][p-1][q][1],num[i][j][p][q][1],inf);

                        connect(num[i][j][p][q][1],num[i+(1<<(p-1))][j][p-1][q][1],0);

                    }

                }

    while (query--) {

        int x1,y1,x2,y2,w,z,q=0,p=0,cur=++tot; scanf("%d%d%d%d%d%d",&x1,&y1,&x2,&y2,&z,&w);

        ans+=w;

        while ((1<<(p+1))<=x2-x1+1) p++;

        while ((1<<(q+1))<=y2-y1+1) q++; 

        if (z) {

            connect(cur,sink,w); connect(sink,cur,0);

            connect(num[x1][y1][p][q][1],cur,inf); connect(cur,num[x1][y1][p][q][1],0);

            connect(num[x1][y2-(1<<q)+1][p][q][1],cur,inf); connect(cur,num[x1][y2-(1<<q)+1][p][q][1],0);

            connect(num[x2-(1<<p)+1][y1][p][q][1],cur,inf); connect(cur,num[x2-(1<<p)+1][y1][p][q][1],0);

            connect(num[x2-(1<<p)+1][y2-(1<<q)+1][p][q][1],cur,inf); connect(cur,num[x2-(1<<p)+1][y2-(1<<q)+1][p][q][1],0);

        } else {

            connect(source,cur,w); connect(cur,source,0);

            connect(cur,num[x1][y1][p][q][0],inf); connect(num[x1][y1][p][q][0],cur,0);

            connect(cur,num[x1][y2-(1<<q)+1][p][q][0],inf); connect(num[x1][y2-(1<<q)+1][p][q][0],cur,0);

            connect(cur,num[x2-(1<<p)+1][y1][p][q][0],inf); connect(num[x2-(1<<p)+1][y1][p][q][0],cur,0);

            connect(cur,num[x2-(1<<p)+1][y2-(1<<q)+1][p][q][0],inf); connect(num[x2-(1<<p)+1][y2-(1<<q)+1][p][q][0],cur,0);

        }

    }

    while (bfs()) ans-=dinic(source,inf);

    printf("%d\n",ans);

    fclose(stdin); fclose(stdout);

    return 0;

}

 

你可能感兴趣的:(【HNOI】d 最小割)