[SDOI2010]粟粟的书架
题意:
一个R * C的矩阵,每个位置都有个数page[ij],现在选定一个小矩阵范围(给左上角坐标,和右下角坐标),问这个范围内的数总和是否大于h,如果大于h的话最少选几个数aij
对于50%的数据,满足R, C≤200,M≤200,000;
另有50%的数据,满足R=1,C≤500,000,M≤20,000;
对于100%的数据,满足1≤Pi,j≤1,000,1≤Hi≤2,000,000,000。
题解:
题目数据给的很奇特,一半是R,C均小于200,另一半R=1(R=1也就是只有一行)
对于前一半数据,我们可以搞一个二位前缀和,二分最小值
value[i][j][k]表示从(1,1)到(i,j)的矩阵中数值>=k的数的总和
num[i][j][k]表示从(1,1)到(i,j)的矩阵中数值>=k的个数
value是为了计算区间总和是否大于h,num用于二分选择数的最小值
num和val的维护就是前缀和经典的容斥原理
if(page[i][j]>=k)value[i][j][k]=value[i-1][j][k]+value[i][j-1][k]-value[i-1][j-1][k]+page[i][j];
else value[i][j][k]=value[i-1][j][k]+value[i][j-1][k]-value[i-1][j-1][k]+0;if(page[i][j]>=k)num[i][j][k]=num[i-1][j][k]+num[i][j-1][k]-num[i-1][j-1][k]+1;
else num[i][j][k]=num[i-1][j][k]+num[i][j-1][k]-num[i-1][j-1][k]+0;
对于后一半数据R=1,C<=5∗1055*10^55∗105,就不能用二位前缀和了,依旧是二分最小值,直接主席树就可以
代码:
#include<bits/stdc++.h>
#define debug(a,b) printf("%s = %d\n",a,b);
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll=1e18;
const int INF_int=0x3f3f3f3f;
inline ll read(){ll s=0,w=1ll;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1ll;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10ll+((ch-'0')*1ll),ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);return s*w;
}
void rd_test(){#ifdef ONLINE_JUDGE#elsestartTime = clock(); //计时开始freopen("in.txt","r",stdin);#endif
}
void Time_test(){#ifdef ONLINE_JUDGE#elseendTime = clock(); //计时结束printf("\n运行时间为:%lfs\n",(double)(endTime - startTime) / CLOCKS_PER_SEC);#endif
}
int r,c,m;
const int maxn=204;
ll val[maxn][maxn][1003];
int page[maxn][maxn];
int num[maxn][maxn][1004];
inline ll getval(int a,int b,int x,int y,int k){return val[x][y][k]-val[x][b-1][k]-val[a-1][y][k]+val[a-1][b-1][k];
}
inline ll getnum(int a,int b,int x,int y,int k){return num[x][y][k]-num[x][b-1][k]-num[a-1][y][k]+num[a-1][b-1][k];
}
void work2(){int maxx=0;for(int i=1;i<=r;i++){for(int j=1;j<=c;j++){scanf("%d",&page[i][j]);maxx=max(maxx,page[i][j]);}}for(int k=0;k<=maxx;k++){for(int i=1;i<=r;i++){for(int j=1;j<=c;j++){if(page[i][j]>=k)val[i][j][k]=val[i-1][j][k]+val[i][j-1][k]-val[i-1][j-1][k]+page[i][j];else val[i][j][k]=val[i-1][j][k]+val[i][j-1][k]-val[i-1][j-1][k]+0;if(page[i][j]>=k)num[i][j][k]=num[i-1][j][k]+num[i][j-1][k]-num[i-1][j-1][k]+1;else num[i][j][k]=num[i-1][j][k]+num[i][j-1][k]-num[i-1][j-1][k]+0;}}}while(m--){int x1,y1,x2,y2;ll h;scanf("%d%d%d%d%lld",&x1,&y1,&x2,&y2,&h);ll ans=getval(x1,y1,x2,y2,0);if(ans<h)printf("Poor QLW\n");else {int l=0,r=maxx+1;while(l+1<r){int mid=(l+r)>>1;if(getval(x1,y1,x2,y2,mid)>=h)l=mid;else r=mid;}int maxnum=getnum(x1,y1,x2,y2,l);printf("%d\n",maxnum-((getval(x1,y1,x2,y2,l)-h)/l));}}
}
const int N=5500002;
int L[N],R[N],size[N],sum[N],rt[N],cnt;
inline void update(int &now,int pre,int l,int r,int x){now=++cnt;size[now]=size[pre]+1;sum[now]=sum[pre]+x;L[now]=L[pre];R[now]=R[pre];if(l==r)return ;int mid=l+r>>1;if(x<=mid)update(L[now],L[pre],l,mid,x);else update(R[now],R[pre],mid+1,r,x);
}
inline ll query(int Lrt,int Rrt,int l,int r,ll k){ll ans=0;while(l<r){int mid=(l+r)>>1;int Sum=sum[R[Rrt]]-sum[R[Lrt]];//优先取较大值,右侧 的数更大 if(Sum<k){ans+=size[R[Rrt]]-size[R[Lrt]];k-=Sum;r=mid;Rrt=L[Rrt];Lrt=L[Lrt]; }else if(Sum>=k){l=mid+1;Rrt=R[Rrt];Lrt=R[Lrt]; }}//debug("(k+l-1)/l",(k+l-1)/l);ans+=(k+l-1)/l;return ans;
}
void work1(){int x;rt[0]=0;for(int i=1;i<=c;i++){scanf("%d",&x);update(rt[i],rt[i-1],1,1000,x);}while(m--){int x1,y1,x2,y2;ll h;scanf("%d%d%d%d%lld",&x1,&y1,&x2,&y2,&h);if(sum[rt[y2]]-sum[rt[y1-1]]<h){printf("Poor QLW\n");continue;}printf("%d\n",query(rt[y1-1],rt[y2],1,1000,h));}
}
int main()
{rd_test();cin>>r>>c>>m;if(r==1)work1();else work2();//Time_test();return 0;
}