zoukankan      html  css  js  c++  java
  • BZOJ1926:[SDOI2010]粟粟的书架

    浅谈主席树:https://www.cnblogs.com/AKMer/p/9956734.html

    题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=1926

    这题应该算是两题……首先贪心的想,我们肯定是尽量选厚的书。对于前(50)%的数据,我们可以统计大于等于(k)的数字的二维前缀和与书的本数,然后二分(k),直接(O(1))计算厚度总和可不可以大于(h),记得,如果对于某个(k),如果这一个子矩阵里的高度总和大于(h),我们还要试着去掉厚度为(k)的书,让厚度总和尽可能逼近(h)才行。因为二分具有单调性,(k)满足而(k+1)不满足,那么你能去掉的厚度为(k)的本数肯定不会大于真实的(k)的本数。

    对于后(50)%的数据,因为书架从矩阵退化成了数列,我们可以用主席树维护。我们在值域上建主席树,(rt[i])表示第(i)个版本的主席树,也就是(1)(i)号书全部插入主席树之后的主席树。对于一个询问[(l,r)],我们先看右儿子的总厚度是否大于(h),是的话就往右儿子走,否则使(h)减去右儿子的总厚度,到左儿子里去找剩下的(h)。当你确定到某一本厚度的时候,像二分那样,只返回要用的最少的本数而不是全部都返回。

    时间复杂度:(O(n^2p+mlogp))---前(50)%,(O((n+m)logn))---后(50)%

    空间复杂度:(O(n^2p))---前(50)%,(O(nlogn))---后(50)%

    代码如下:

    #include <cstdio>
    using namespace std;
    
    const int maxn=5e5+5;
    
    int n,m,q,x1,x2;
    int p1[205][205];
    int p2[maxn],rt[maxn];
    int sum[205][205][1005],cnt[205][205][1005];
    
    int read() {
    	int x=0,f=1;char ch=getchar();
    	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    	return x*f;
    }
    
    void init() {
    	n=read(),m=read(),q=read();
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			if(n!=1)p1[i][j]=read();
    			else p2[j]=read();
    }
    
    int calc(int (*a)[205][1005],int x1,int y1,int x2,int y2,int k) {
    	return a[x2][y2][k]-a[x1-1][y2][k]-a[x2][y1-1][k]+a[x1-1][y1-1][k];
    }
    
    void solve1() {
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			for(int k=0;k<=1000;k++) {
    				sum[i][j][k]=sum[i][j-1][k]+sum[i-1][j][k]-sum[i-1][j-1][k];
    				cnt[i][j][k]=cnt[i][j-1][k]+cnt[i-1][j][k]-cnt[i-1][j-1][k];
    				if(p1[i][j]>=k) sum[i][j][k]+=p1[i][j],cnt[i][j][k]++;
    			}
    	for(int i=1;i<=q;i++) {
    		int x1=read(),y1=read(),x2=read(),y2=read(),h=read();
    		int l=0,r=1000,ans=-1;
    		while(l<r) {
    			int mid=(l+r+1)>>1;
    			int tmp=calc(sum,x1,y1,x2,y2,mid);
    			if(tmp<h)r=mid-1;
    			else  {
    				l=mid,ans=calc(cnt,x1,y1,x2,y2,mid);
    				ans-=(tmp-h)/mid;//直接减,不要虚
    			}
    		}
    		if(ans==-1)puts("Poor QLW");
    		else printf("%d
    ",ans);
    	}
    }
    
    struct tree_node {
    	int cnt,sum,ls,rs;
    };
    
    struct chairman_tree {
    	int tot;
    	tree_node tree[maxn*20];
    	
    	void ins(int lst,int &now,int l,int r,int pos) {
    		now=++tot;tree[now]=tree[lst];
            tree[now].cnt++,tree[now].sum+=pos;
    		if(l==r)return;
    		int mid=(l+r)>>1;
    		if(pos<=mid)ins(tree[lst].ls,tree[now].ls,l,mid,pos);
    		else ins(tree[lst].rs,tree[now].rs,mid+1,r,pos);
    	}
    
    	int query(int L,int R,int l,int r,int limit) {
    		if(tree[R].sum-tree[L].sum<limit)return -1;
    		if(l==r) {
    			int tmp=tree[R].cnt-tree[L].cnt;
    			int fake=tree[R].sum-tree[L].sum;
    			return tmp-(fake-limit)/l;//本数减去可以去掉的数量
    		}
    		int mid=(l+r)>>1,tmp=tree[tree[R].rs].sum-tree[tree[L].rs].sum;
    		int fake=tree[tree[R].rs].cnt-tree[tree[L].rs].cnt;
    		if(tmp>=limit)return query(tree[L].rs,tree[R].rs,mid+1,r,limit);
    		else return query(tree[L].ls,tree[R].ls,l,mid,limit-tmp)+fake;
    		return 0;
    	}
    }T;
    
    void solve2() {
    	for(int i=1;i<=m;i++)
    		T.ins(rt[i-1],rt[i],1,1000,p2[i]);
    	for(int i=1;i<=q;i++) {
    		x1=read();int l=read();x2=read();int r=read(),h=read();
    		int ans=T.query(rt[l-1],rt[r],1,1000,h);
    		if(ans==-1)puts("Poor QLW");
    		else printf("%d
    ",ans);
    	}
    }
    
    int main() {
    	init();
    	if(n!=1)solve1();
    	else solve2();
    	return 0;
    }
    
  • 相关阅读:
    iOS开发拓展篇—音频处理(音乐播放器1)
    iOS开发拓展篇—CoreLocation地理编码
    iOS开发拓展篇—CoreLocation定位服务
    iOS开发拓展篇—CoreLocation简单介绍
    iOS开发拓展篇—封装音频文件播放工具类
    图标框架Font Awesome
    WordPress基础:设置后台语言
    使用帝国备份王备份还原网站数据
    WordPress主题开发:加载脚本和样式
    WordPress主题开发:get_term_by和get_term_link
  • 原文地址:https://www.cnblogs.com/AKMer/p/9958122.html
Copyright © 2011-2022 走看看