[SDOI2010]粟粟的书架
[SDOI2010]粟粟的书架
题目大意:
给定一个\(n\times m\)的格子,每个格子有一个权值\(p_{i,j}\)。\(q\)次询问,每次询问给定的子矩阵中,至少选取多少个数使得这些数权值和\(\le h\)?
对于\(50\%\)的数据,\(n,m\le200,q\le2\times10^5\);
对于另外\(50\%\)的数据,\(n=1,m\le5\times10^5,q\le2\times10^4\)。
思路:
二合一题目。
对于前\(50\%\)的数据,预处理矩阵\(1\sim i,1\sim j\)中大于\(p\)的数的个数及权值和。二分答案判断可行性即可。
对于后\(50\%\)的数据,主席树套二分即可。
源代码:
#include<cstdio>
#include<cctype>
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
const int N=201,M=5e5+1,P=1001,logP=11;
int sum[N][N][P],cnt[N][N][P];
inline int calc1(const int &x1,const int &y1,const int &x2,const int &y2,const int &k) {
return cnt[x2][y2][k]-cnt[x1][y2][k]-cnt[x2][y1][k]+cnt[x1][y1][k];
}
inline int calc2(const int &x1,const int &y1,const int &x2,const int &y2,const int &k) {
return sum[x2][y2][k]-sum[x1][y2][k]-sum[x2][y1][k]+sum[x1][y1][k];
}
inline bool check1(const int &x1,const int &y1,const int &x2,const int &y2,const int &k,const int &h) {
return calc2(x1,y1,x2,y2,k)>=h;
}
inline bool check2(const int &x1,const int &y1,const int &x2,const int &y2,const int &k,const int &c,const int &h) {
return calc2(x1,y1,x2,y2,k)-c*k>=h;
}
class FotileTree {
#define mid ((b+e)>>1)
private:
struct Node {
int sum,cnt,left,right;
};
Node node[M*logP];
int sz,new_node(const int &p) {
node[++sz]=node[p];
return sz;
}
public:
int root[M];
void insert(int &p,const int &b,const int &e,const int &x) {
p=new_node(p);
node[p].cnt++;
node[p].sum+=x;
if(b==e) return;
if(x<=mid) insert(node[p].left,b,mid,x);
if(x>mid) insert(node[p].right,mid+1,e,x);
}
int query(const int &p,const int &q,const int &b,const int &e,const int &x) const {
if(node[p].sum-node[q].sum<x) return -1;
if(b==e) {
int l=1,r=node[p].cnt;
while(l<=r) {
const int k=(l+r)>>1;
if(node[p].sum-k*b>=x) {
l=k+1;
} else {
r=k-1;
}
}
return node[p].cnt-(l-1);
}
const int tmp=node[node[p].right].sum-node[node[q].right].sum;
if(tmp>=x) return query(node[p].right,node[q].right,mid+1,e,x);
return query(node[p].left,node[q].left,b,mid,x-tmp)+node[node[p].right].cnt-node[node[q].right].cnt;
}
#undef mid
};
FotileTree t;
int main() {
const int n=getint(),m=getint(),q=getint();
if(n==1) {
for(register int i=1;i<=m;i++) {
t.insert(t.root[i]=t.root[i-1],1,P-1,getint());
}
for(register int i=0;i<q;i++) {
const int x1=getint(),y1=getint(),x2=getint(),y2=getint(),h=getint();
const int ans=t.query(t.root[y2],t.root[y1-1],1,P-1,h);
if(ans==-1) {
puts("Poor QLW");
continue;
}
printf("%d\n",ans);
}
return 0;
}
for(register int i=1;i<=n;i++) {
for(register int j=1;j<=m;j++) {
const int p=getint();
cnt[i][j][p]=cnt[i-1][j][p]+cnt[i][j-1][p]-cnt[i-1][j-1][p]+1;
sum[i][j][p]=sum[i-1][j][p]+sum[i][j-1][p]-sum[i-1][j-1][p]+p;
for(register int k=1;k<P;k++) {
if(k==p) continue;
cnt[i][j][k]=cnt[i-1][j][k]+cnt[i][j-1][k]-cnt[i-1][j-1][k];
sum[i][j][k]=sum[i-1][j][k]+sum[i][j-1][k]-sum[i-1][j-1][k];
}
}
}
for(register int i=1;i<=n;i++) {
for(register int j=1;j<=m;j++) {
for(register int p=P-2;p>=1;p--) {
cnt[i][j][p]+=cnt[i][j][p+1];
sum[i][j][p]+=sum[i][j][p+1];
}
}
}
for(register int i=0;i<q;i++) {
const int x1=getint()-1,y1=getint()-1,x2=getint(),y2=getint(),h=getint();
if(calc2(x1,y1,x2,y2,1)<h) {
puts("Poor QLW");
continue;
}
int l=1,r=P-1,ans=-1;
while(l<=r) {
const int mid1=(l+r)>>1;
if(check1(x1,y1,x2,y2,mid1,h)) {
l=mid1+1;
int b=1,e=calc1(x1,y1,x2,y2,mid1)-(mid1!=P-1?calc1(x1,y1,x2,y2,mid1+1):0);
while(b<=e) {
const int mid2=(b+e)>>1;
if(check2(x1,y1,x2,y2,mid1,mid2,h)) {
b=mid2+1;
} else {
e=mid2-1;
}
}
ans=calc1(x1,y1,x2,y2,mid1)-(b-1);
} else {
r=mid1-1;
}
}
printf("%d\n",ans);
}
return 0;
}