传送门
对于第一问(R,C<=200);
预处理f[x][y][k],s[x][y][k].表示从(1,1)到(x,y)中大于等于k的数的和与大于等于k的数的个数。
然后二分最小的数即可。
对于第二问(R=1):
我们还是二分最小数。
判断就变成了询问一段区间内大于等于x的数的和以及它们的个数。
显然主席树可以处理这个
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#define N 11000000
#define M 210
using namespace std;
int n,m,T,a[M][M][M*5],b[M][M][M*5],c[M][M],x1,y1,x2,y2,h;
int t,rt[550000],ls[N],rs[N],v[N],s[N],cnt;
void work1(){
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++) scanf("%d",&c[i][j]);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
for (int k=1;k<=1000;k++){
a[i][j][k]=a[i][j-1][k]+a[i-1][j][k]-a[i-1][j-1][k];
b[i][j][k]=b[i][j-1][k]+b[i-1][j][k]-b[i-1][j-1][k];
if (c[i][j]>=k) a[i][j][k]++,b[i][j][k]+=c[i][j];
}
while (T--){
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);
int l=0,r=1001; x1--; y1--;
while (l+1<r){
int mid=(l+r)/2;
if (b[x1][y1][mid]+b[x2][y2][mid]-b[x1][y2][mid]-b[x2][y1][mid]>=h)
l=mid; else r=mid;
}
if (l==0) puts("Poor QLW");
else{
int sum1=b[x1][y1][l]+b[x2][y2][l]-b[x2][y1][l]-b[x1][y2][l]-h;
int sum2=a[x1][y1][l]+a[x2][y2][l]-a[x2][y1][l]-a[x1][y2][l];
printf("%d\n",sum2-sum1/l);
}
}
}
void add(int l,int r,int x,int &y,int k){
y=++cnt; v[y]=v[x]+1; s[y]=s[x]+k;
if (l==r) return;
int mid=(l+r)/2;
if (k<=mid) rs[y]=rs[x],add(l,mid,ls[x],ls[y],k);
else ls[y]=ls[x],add(mid+1,r,rs[x],rs[y],k);
}
int ask(int x,int y,int k){
int l=1,r=1000,ok=0;
x=rt[x-1]; y=rt[y];
if (s[y]-s[x]<h) return -1;
while (l<r){
int mid=(l+r)/2,t=s[rs[y]]-s[rs[x]];
if (t<k){
ok+=v[rs[y]]-v[rs[x]]; k-=t; r=mid;
x=ls[x]; y=ls[y];
}
else{
l=mid+1; x=rs[x]; y=rs[y];
}
}
ok+=(k+l-1)/l;
return ok;
}
void work2(){
for (int i=1,x;i<=m;i++){
scanf("%d",&x);
add(1,1000,rt[i-1],rt[i],x);
}
while (T--){
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);
int ans=ask(y1,y2,h);
if (ans==-1) puts("Poor QLW");
else printf("%d\n",ans);
}
}
int main(){
scanf("%d%d%d",&n,&m,&T);
if (n!=1) work1(); else work2();
}