1926: 粟粟的书架 前缀和+二分+主席树

题目大意:

http://www.lydsy.com/JudgeOnline/problem.php?id=1926

题解:

我们发现这道题其实是两问
第一问是对于 \(R, C\leq 200,M\leq 200,000\)
是在矩形上的询问
第二问是对于 \(R=1,C\leq 500,000,M\leq 20,000\)
是在序列上的询问
所以我们写两个程序即可
首先我们来解决序列上的问题:
很明显我们在主席树上二分查找即可.\(O(mlogn)\)可做
然后我们来解决在矩形上的问题:
很明显我们写个二维差分主席树就好了啦
我们发现这个子任务的特点是R,C的范围很小所以我们考虑预处理
我们可以预处理出\(f[i][j][k]\)表示在二维前缀和\((i,j)\)
大于等于\(k\)的数的和,这样我们就可以二分啦!
同时为了统计答案我们还要处理出\(g[i][j][k]\)表示>=k的数的个数
然后这道题就华丽丽地解决啦
我们还有一个问题!
对于序列:2 2 2 2我们取多少才能达到高度\(2\),很明显取1个
但是程序会输出4
这个东西再二分一下还应该删除多少个不久好了吗。。。
竟然1A了

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
	x=0;char ch;bool flag = false;
	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
int n,m,q;
namespace work1{
	const int maxn = 201;
	int a[maxn][maxn];
	int f[201][201][1001];
	int g[201][201][1001];
	int lim = 0,x1,y1,x2,y2;
	inline int calc_f(int k){
		return f[x2][y2][k] - f[x1-1][y2][k] - f[x2][y1-1][k] + f[x1-1][y1-1][k];
	}
	inline int calc_g(int k){
		return g[x2][y2][k] - g[x1-1][y2][k] - g[x2][y1-1][k] + g[x1-1][y1-1][k];
	}
	int main(){
		for(int i=1;i<=n;++i){
			for(int j=1;j<=m;++j){
				read(a[i][j]);
				lim = max(lim,a[i][j]);
			}
		}
		for(int i=1;i<=n;++i){
			for(int j=1;j<=m;++j){
				for(int k=1;k<=lim;++k){
					f[i][j][k] = f[i-1][j][k] + f[i][j-1][k] - f[i-1][j-1][k];
					g[i][j][k] = g[i-1][j][k] + g[i][j-1][k] - g[i-1][j-1][k];
				}
				for(int k=1;k<=a[i][j];++k){
					f[i][j][k] += a[i][j];
					g[i][j][k]++;
				}
			}
		}
		while(q--){
			read(x1);read(y1);read(x2);read(y2);
			int h;read(h);
			int l = 1,r = lim,ans = -1;
			while(l <= r){
				int mid = (l+r) >> 1;
				if(calc_f(mid) >= h) ans = mid,l = mid+1;
				else r = mid-1;
			}if(ans == -1) puts("Poor QLW");
			else{
				int x = calc_f(ans) - calc_f(ans+1);x /= ans;
				int y = calc_g(ans);
				int z = calc_f(ans);
				l = 0,r = x;x = 0;
				while(l <= r){
					int mid = (l+r) >> 1;
					if(z - mid*ans >= h) x = mid,l = mid+1;
					else r = mid-1;
				}y -= x;
				printf("%d\n",y);
			}
		}
		return 0;
	}
}
namespace work2{
	const int maxn = 500010;
	typedef pair<int,int> pa;
	struct Node{
		Node *ch[2];
		int sum,num;
	}*null;
	inline void init(){
		null = new Node;
		null->ch[0] = null->ch[1] = null;
		null->sum = null->num = 0;
	}
	Node *rt[maxn];
	inline Node* insert(Node *rt,int l,int r,int val){
		Node *p = new Node();(*p) = (*rt);
		if(l == r){
			p->sum += val;
			p->num ++ ;
			return p;
		}int mid = (l+r) >> 1;
		if(val <= mid) p->ch[0] = insert(rt->ch[0],l,mid,val);
		else p->ch[1] = insert(rt->ch[1],mid+1,r,val);
		p->sum = p->ch[0]->sum + p->ch[1]->sum;
		p->num = p->ch[0]->num + p->ch[1]->num;
		return p;
	}int pos = -1,cnt = -1;
	inline pa query(Node *x,Node *y,int l,int r,int h){
		if(l == r){
			pos = l;cnt = y->num - x->num;
			return make_pair(y->sum - x->sum,y->num - x->num);
		}
		int mid = (l+r) >> 1,sum = y->ch[1]->sum - x->ch[1]->sum;
		if(sum < h){
			pa tmp = query(x->ch[0],y->ch[0],l,mid,h-sum);
			tmp.first += y->ch[1]->sum - x->ch[1]->sum;
			tmp.second += (y->ch[1]->num - x->ch[1]->num);
			return tmp;
		}else return query(x->ch[1],y->ch[1],mid+1,r,h);
	}
	int main(){
		init();n = m;rt[0] = null;
		for(int i=1,x;i<=n;++i){
			read(x);
			rt[i] = insert(rt[i-1],1,1000,x);
		}
		while(q--){
			int s,t;read(s);read(s);read(t);read(t);
			int h;read(h);
			pa x = query(rt[s-1],rt[t],1,1000,h);
			if(x.first < h){puts("Poor QLW");continue;}
			int l = 0,r = cnt,y = 0;
			while(l <= r){
				int mid = (l+r) >> 1;
				if(x.first - mid*pos >= h) y = mid,l = mid+1;
				else r = mid-1;
			}
			printf("%d\n",x.second - y);
		}
		return 0;
	}
}
int main(){
	read(n);read(m);read(q);
	if(n != 1) work1::main();
	else work2::main();
	getchar();getchar();
	return 0;
}

不得不说当时我开数组的时候浑身难受
一两个数组就100多MB下去了...

posted @ 2017-02-20 19:32  Sky_miner  阅读(170)  评论(0编辑  收藏  举报