[SDOI2010]粟粟的书架
Posted Jozky86
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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
∗
1
0
5
5*10^5
5∗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
#else
startTime = clock(); //计时开始
freopen("in.txt","r",stdin);
#endif
}
void Time_test(){
#ifdef ONLINE_JUDGE
#else
endTime = 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;
}
以上是关于[SDOI2010]粟粟的书架的主要内容,如果未能解决你的问题,请参考以下文章