P2468 [SDOI2010]粟粟的书架

本文详细解析了SDOI2010栗子书架问题,通过分情况讨论策略,介绍了如何使用二维前缀和与主席树解决矩阵中找到指定高度书本数量的问题,适用于R,C<=200和R=1两种特殊情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

P2468 [SDOI2010]粟粟的书架

题意

给一个n*m的矩阵,每个位置里有一本书,每本书有一个高度,在给定的矩阵中找最少要几本书才能使高度大于等于h

思路

分情况讨论,首先对于R,C<=200: sum[i][j][k]表示对于子矩阵1-i ,1-j的子矩阵中有多少大于等于k值的和,cal[i][j][k]表示对于子矩阵1-i ,1-j的子矩阵中有多少大于等于k值的个数,然后二分找最少的书.

对于R=1的情况,用主席树解决.(这个代码也是借鉴别人的,写出来就很像了).洛谷题解可以多看看洛谷的题解

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=500000;
int a[210][210],[210][210][1010],cal[210][210][1010];
int n,m,q;
int get_sum(int a,int b,int c,int d,int h){
	return sum[c][d][h]-sum[c][b-1][h]-sum[a-1][d][h]+sum[a-1][b-1][h];
}
int get_val(int a,int b,int c,int d,int h){
	return cal[c][d][h]-cal[c][b-1][h]-cal[a-1][d][h]+cal[a-1][b-1][h];
}
int cnt=0,root[maxn];
struct node{
	int l,r,ls,rs,sum,w;
}t[40*maxn];
int build(int l,int r){
	int k=++cnt;
	// printf("l=%d r=%d\n",l,r);
	t[k].l=l,t[k].r=r;
	if(l==r) return k;
	int mid=l+r>>1;
	t[k].ls=build(l,mid);
	t[k].rs=build(mid+1,r);
	return k;
}
int add(int last,int x){
	int k=++cnt;
	t[k]=t[last];
	t[k].w++;
	t[k].sum+=x;
	if(t[k].l==t[k].r) return k;
	int mid=t[k].l+t[k].r>>1;
	if(x<=mid) t[k].ls=add(t[k].ls,x);
	else t[k].rs=add(t[k].rs,x);
	return k;
}
int query(int k1,int k2,int k){
	if(t[k1].l==t[k1].r) return (k-1)/t[k1].l+1;
	int w=t[t[k2].rs].sum-t[t[k1].rs].sum;
	if(k<=w) return query(t[k1].rs,t[k2].rs,k);
	else return query(t[k1].ls,t[k2].ls,k-w)+t[t[k2].rs].w-t[t[k1].rs].w;
}
int main(){
	scanf("%d%d%d",&n,&m,&q);
	if(n!=1){
		int maxx=-1;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				scanf("%d",&a[i][j]),maxx=max(maxx,a[i][j]);
		for(int k=1;k<=maxx;k++)
			for(int i=1;i<=n;i++){
				for(int j=1;j<=m;j++){
					if(a[i][j]>=k){
						sum[i][j][k]=sum[i-1][j][k]+sum[i][j-1][k]-sum[i-1][j-1][k]+a[i][j];
						cal[i][j][k]=cal[i-1][j][k]+cal[i][j-1][k]-cal[i-1][j-1][k]+1;
					}
					else {
						sum[i][j][k]=sum[i-1][j][k]+sum[i][j-1][k]-sum[i-1][j-1][k];
						cal[i][j][k]=cal[i-1][j][k]+cal[i][j-1][k]-cal[i-1][j-1][k];
					}
				}
			}
		while(q--){
			int a,b,c,d,h;scanf("%d%d%d%d%d",&a,&b,&c,&d,&h);
			if(get_sum(a,b,c,d,1)<h){
				printf("Poor QLW\n");
				continue;
			}
			int l=1,r=maxx;
			while(l<r){
				int mid=(l+r+1)>>1;
				if(get_sum(a,b,c,d,mid)>=h) l=mid;
				else r=mid-1;
			}
			int ans=get_val(a,b,c,d,l)-(get_sum(a,b,c,d,l)-h)/l;
			printf("%d\n",ans);
		}
	}
	else{
		cnt=0;
		root[0]=1;
		build(1,1000);
		for(int i=1,x;i<=m;i++){
			scanf("%d",&x);
			root[i]=cnt+1;
			add(root[i-1],x);
		}
		while(q--){
			int a,b,c,d,h;scanf("%d%d%d%d%d",&a,&b,&c,&d,&h);
			b--;
			if(t[root[d]].sum-t[root[b]].sum<h) printf("Poor QLW\n");
			else printf("%d\n",query(root[b],root[d],h));
		}
	}
	//system("pause");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值