Description

Data Constraint

Solution
遇到这种求第k小的题我们可以采用堆得方法。显然,对于一个左上角为(x,y),右下角为(x1,y1)的和谐矩阵,它的权值一定比{(x-1,y),(x1,y1)}{(x,y-1),(x1,y1)}{(x,y),(x1+1,y1)}{(x,y),(x1,y1+1)}这四个矩形都要小,因为矩形中的每个数都为非负整数。那么,我们一开始把所有唱的为Mina,宽为Minb的矩形全部放入一个堆中。每次取堆顶,把它往上下左右进行扩展,把扩展后的放入堆中,并把堆顶去除。一直这样做直到求到第k小。注意,为了避免重复,我们每次扩展的方向不能为4个,改为向下和向右即可。
代码
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const long long maxn=1005;
const int f1[2][5]={{0,-1,0,0,0},{0,0,-1,0,0}};
long long a[maxn][maxn],f[maxn][maxn],n,m,i,t,j,k,l,ma,mb,p,x,y,q,num;
long long d[maxn*maxn][7];
void jia(int k,int i,int j,int x,int y,long long t,int z){
d[k][1]=i;d[k][2]=j;d[k][3]=x;d[k][4]=y;d[k][6]=t,d[k][5]=z;
}
long long suan(int x,int y,int i,int j){
return f[x][y]-f[i-1][y]-f[x][j-1]+f[i-1][j-1];
}
void up(int x){
int i,j,k,l;
while (x!=1 && d[x][6]<d[x/2][6]){
for (k=1;k<=6;k++)
swap(d[x][k],d[x/2][k]);
x/=2;
}
}
void down(int x){
int i,j,k,l,t;
while (x*2<=num && d[x*2][6]<d[x][6] || x*2<num && d[x*2+1][6]<d[x][6]){
t=x*2;
if (d[t][6]>d[t+1][6]) t++;
for (k=1;k<=6;k++)
swap(d[x][k],d[t][k]);
x=t;
}
}
int main(){
scanf("%d%d%d%d%d",&n,&m,&ma,&mb,&q);
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
scanf("%d",&a[i][j]),f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+a[i][j];
for (i=1;i<=n-ma+1;i++)
for (j=1;j<=m-mb+1;j++){
x=i+ma-1;y=j+mb-1;
t=f[x][y]-f[i-1][y]-f[x][j-1]+f[i-1][j-1];
jia(++num,i,j,x,y,t,0);
up(num);
}
for (i=1;i<q;i++){
for (j=d[1][5];j<=1;j++){
++num;t=0;
for (k=1;k<=4;k++){
d[num][k]=d[1][k]+f1[j][k];
if (d[num][k]<1){
num--;t=1;
break;
}
}
if (t) continue;
d[num][5]=j;d[num][6]=suan(d[num][3],d[num][4],d[num][1],d[num][2]);
up(num);
}
for (k=1;k<=6;k++)
d[1][k]=d[num][k];
num--;
down(1);
}
printf("%lld\n",d[1][6]);
}