zoukankan      html  css  js  c++  java
  • 【刷题】BZOJ 1926 [Sdoi2010]粟粟的书架

    Description

    幸福幼儿园 B29 班的粟粟是一个聪明机灵、乖巧可爱的小朋友,她的爱好是画画和读书,尤其喜欢 Thomas H. Cormen 的文章。粟粟家中有一个 R行C 列的巨型书架,书架的每一个位置都摆有一本书,上数第i 行、左数第j 列摆放的书有Pi,j页厚。粟粟每天除了读书之外,还有一件必不可少的工作就是摘苹果,她每天必须摘取一个指定的苹果。粟粟家果树上的苹果有的高、有的低,但无论如何凭粟粟自己的个头都难以摘到。不过她发现, 如果在脚下放上几本书,就可以够着苹果;她同时注意到,对于第 i 天指定的那个苹果,只要她脚下放置书的总页数之和不低于Hi,就一定能够摘到。由于书架内的书过多,父母担心粟粟一天内就把所有书看完而耽误了上幼儿园,于是每天只允许粟粟在一个特定区域内拿书。这个区域是一个矩形,第 i 天给定区域的左上角是上数第 x1i行的左数第 y1i本书,右下角是上数第 x2i行的左数第y2i本书。换句话说,粟粟在这一天,只能在这﹙x2i-x1i+1﹚×﹙y2i-y1i+1﹚本书中挑选若干本垫在脚下,摘取苹果。粟粟每次取书时都能及时放回原位,并且她的书架不会再撤下书目或换上新书,摘苹果的任务会一直持续 M天。给出每本书籍的页数和每天的区域限制及采摘要求,请你告诉粟粟,她每天至少拿取多少本书,就可以摘到当天指定的苹果。

    Input

    第一行是三个正整数R,C,M。
    接下来是一个R行C列的矩阵,从上到下、从左向右依次给出了每本书的页数Pi,j。
    接下来M行,第i行给出正整数x1i,y1i,x2i,y2i,Hi,表示第i天的指定区域是﹙x1i,y1i﹚与﹙x2i,y2i﹚间
    的矩形,总页数之和要求不低于Hi。
    保证1≤x1i≤x2i≤R,1≤y1i≤y2i≤C。

    Output

    有M行,第i 行回答粟粟在第 i 天时为摘到苹果至少需要 拿取多少本书。如果即使取走所有书都无法摘到苹果,
    则在该行输出“Poor QLW” (不含引号)。

    Sample Input

    5 5 7
    14 15 9 26 53
    58 9 7 9 32
    38 46 26 43 38
    32 7 9 50 28
    8 41 9 7 17
    1 2 5 3 139
    3 1 5 5 399
    3 3 4 5 91
    4 1 4 1 33
    1 3 5 4 185
    3 3 4 3 23
    3 1 3 3 108

    Sample Output

    6
    15
    2
    Poor QLW
    9
    1
    3

    HINT

    对于 10%的数据,满足 R, C≤10;
    对于 20%的数据,满足 R, C≤40;
    对于 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

    Solution

    又是一道神题
    一开始我都没注意到这道题的100分是由两个50分加起来的。。。
    所以把这两部分分开做
    第一部分:
    对于(R)(C)都小于200的,定义两个数组
    (val[i][j][k])存的是对角为((1,1))((i,j))的矩阵中所有值大于等于(k)的数的值之和
    (num[i][j][k])存的是对角为((1,1))((i,j))的矩阵中所有值大于等于(k)的数的个数之和
    两个东东都可以前缀和的
    处理完这两个东西之后开始二分
    我们二分拿的所有书中厚度最小的那本书的厚度
    然后用容斥看看限定的矩阵是否可以满足我们二分的厚度
    二分出那个答案后,因为我们二分的是最小厚度,而厚度等于最小厚度的可能有很多本,所以我们不需要把厚度等于最小厚度的书全部加上,而是只要拿走一部分就行了
    这一部分是多少呢?
    我们不是二分出了最小厚度(d)
    那么我们先把厚度大于等于(d+1)的书全部取走,并且这些书的厚度之和为(s)
    然后对于厚度等于(d)的,我们除一下(()(h-s)(剩下的高度)(/) (d)(每本书的高度) ())再向上取整就可以了
    两个加起来就是答案
    第二部分:
    数据形式就是一个数列
    同样我们需要得到上一部分的一系列东西
    可是暴力做会爆时间
    那么就用主席树吧
    主席树维护
    (val)(A_1)(A_i)出现的所有数的和)
    (sum)(A_1)(A_i)总共出现了多少个数)
    每次询问的时候,我们判断当前区间的右儿子区间的(val)是否已经满足条件,满足就更细致地搜右儿子区间
    否则,直接把右儿子的(num)加上,然后搜左儿子区间

    #include<bits/stdc++.h>
    #define ll long long
    #define db double
    #define ld long double
    #define Mid ((l+r)>>1)
    #define lson l,Mid
    #define rson Mid+1,r
    const int MAXN=200+10,MAXH=1000+10,MAXC=501000+10;
    int val[MAXN][MAXN][MAXH],num[MAXN][MAXN][MAXH],n,m,q,B[MAXN][MAXN],A[MAXC],sum[MAXC];
    inline int updiv(int x,int y)
    {
    	return x/y+(x%y?1:0);
    }
    struct ChairMan_Tree{
    	int cnt,lc[MAXC*20],rc[MAXC*20],sum[MAXC*20],val[MAXC*20],root[MAXC];
    	inline void init()
    	{
    		cnt=0;
    		memset(lc,0,sizeof(lc));
    		memset(rc,0,sizeof(rc));
    		memset(sum,0,sizeof(sum));
    		memset(val,0,sizeof(val));
    	}
    	inline void Build(int &rt,int l,int r)
    	{
    		rt=++cnt;
    		sum[rt]=val[rt]=0;
    		if(l==r)return ;
    		Build(lc[rt],lson);
    		Build(rc[rt],rson);
    	}
    	inline void Insert(int &rt,int l,int r,int last,int pos)
    	{
    		rt=++cnt;
    		lc[rt]=lc[last];
    		rc[rt]=rc[last];
    		sum[rt]=sum[last]+1;
    		val[rt]=val[last]+pos;
    		if(l==r)return ;
    		else
    		{
    			if(pos<=Mid)Insert(lc[rt],lson,lc[last],pos);
    			else Insert(rc[rt],rson,rc[last],pos);
    		}
    	}
    	inline int Query(int now,int last,int l,int r,int nd)
    	{
    		if(l==r)return updiv(nd,l);
    		else
    		{
    			int ts=sum[rc[now]]-sum[rc[last]],tv=val[rc[now]]-val[rc[last]],res=0;
    			if(nd>tv)res+=ts+Query(lc[now],lc[last],lson,nd-tv);
    			else res+=Query(rc[now],rc[last],rson,nd);
    			return res;
    		}
    	}
    };
    ChairMan_Tree T;
    template<typename T> inline void read(T &x)
    {
    	T data=0,w=1;
    	char ch=0;
    	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    	if(ch=='-')w=-1,ch=getchar();
    	while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
    	x=data*w;
    }
    template<typename T> inline void write(T x,char c='')
    {
    	if(x<0)putchar('-'),x=-x;
    	if(x>9)write(x/10);
    	putchar(x%10+'0');
    	if(c!='')putchar(c);
    }
    template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
    template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
    template<typename T> inline T min(T x,T y){return x<y?x:y;}
    template<typename T> inline T max(T x,T y){return x>y?x:y;}
    inline int G(int s[MAXN][MAXN][MAXH],int ux,int uy,int lx,int ly,int st)
    {
    	return s[lx][ly][st]-s[ux-1][ly][st]-s[lx][uy-1][st]+s[ux-1][uy-1][st];
    }
    inline void solve1()
    {
    	for(register int i=1;i<=n;++i)
    		for(register int j=1;j<=m;++j)
    		{
    			read(B[i][j]);
    			val[i][j][B[i][j]]+=B[i][j];
    			num[i][j][B[i][j]]++;
    			for(register int k=1;k<=1000;++k)
    			{
    				val[i][j][k]+=val[i-1][j][k]+val[i][j-1][k]-val[i-1][j-1][k];
    				num[i][j][k]+=num[i-1][j][k]+num[i][j-1][k]-num[i-1][j-1][k];
    			}
    		}
    	for(register int i=1;i<=n;++i)
    		for(register int j=1;j<=m;++j)
    			for(register int k=1000;k>=1;--k)
    			{
    				val[i][j][k]+=val[i][j][k+1];
    				num[i][j][k]+=num[i][j][k+1];
    			}
    	while(q--)
    	{
    		int ux,uy,lx,ly,h;
    		read(ux);read(uy);read(lx);read(ly);read(h);
    		int l=1,r=1000,res=-1;
    		while(l<=r)
    		{
    			int mid=(l+r)>>1;
    			if(G(val,ux,uy,lx,ly,mid)>=h)res=mid,l=mid+1;
    			else r=mid-1;
    		}
    		if(!(~res))puts("Poor QLW");
    		else
    		{
    			int least=G(val,ux,uy,lx,ly,res+1),ans=G(num,ux,uy,lx,ly,res+1);
    			ans+=updiv(h-least,res);
    			write(ans,'
    ');
    		}
    	}
    }
    inline void solve2()
    {
    	T.init();
    	for(register int i=1;i<=m;++i)
    	{
    		read(A[i]);
    		sum[i]=sum[i-1]+A[i];
    		T.Insert(T.root[i],1,MAXC,T.root[i-1],A[i]);
    	}
    	while(q--)
    	{
    		int l,r,h;
    		read(l);read(l);read(r);read(r);read(h);
    		if(sum[r]-sum[l-1]<h)puts("Poor QLW");
    		else write(T.Query(T.root[r],T.root[l-1],1,MAXC,h),'
    ');
    	}
    }
    int main()
    {
    	read(n);read(m);read(q);
    	if(n!=1)solve1();
    	else solve2();
    	return 0;
    }
    
  • 相关阅读:
    使用javap分析Java的字符串操作
    使用javap深入理解Java整型常量和整型变量的区别
    分享一个WebGL开发的网站-用JavaScript + WebGL开发3D模型
    Java动态代理之InvocationHandler最简单的入门教程
    Java实现 LeetCode 542 01 矩阵(暴力大法,正反便利)
    Java实现 LeetCode 542 01 矩阵(暴力大法,正反便利)
    Java实现 LeetCode 542 01 矩阵(暴力大法,正反便利)
    Java实现 LeetCode 541 反转字符串 II(暴力大法)
    Java实现 LeetCode 541 反转字符串 II(暴力大法)
    Java实现 LeetCode 541 反转字符串 II(暴力大法)
  • 原文地址:https://www.cnblogs.com/hongyj/p/8626446.html
Copyright © 2011-2022 走看看