【主席树】洛谷P2468 [SDOI2010]粟粟的书架
分析
前50%的数据
val[i][j][k] 表示 [1,1]~[i,j] 大于等于 k 的值 的总和;
size[i][j][k] 表示 [1,1]~[i,j] 大于等于 k 的值 的个数;
在 [1,1000] 之内二分查找;
后50%的数据
建立主席树(可持久化权值线段树),节点信息除了左右子节点还有 区间个数size 与 区间和val;
当插入一个值 pos 时,递归到末节点,size++,val+=pos;
注意
若所给数字都是没有重复的,那查找就很好做;
但是题目说明有重复,假设题给目标值 tar,应当查找在所给区间从大到小选择数字,一选就是一打(一打意思就是选择一个数字 num,无论 num 有几个都选上),选择直到刚刚好小于目标值,再选一个就超过目标值了;
记录现在已经选的数字总和 sum,数字个数 size,最小数字 minn 和 再选择一个数字 num 就超过题给目标值;
由于 num 可能不止一个,所以得一次次选择 num 直到达到目标值;
代码
#include <bits/stdc++.h>
using namespace std;
#define MS 500009
#define ls rt<<1
#define rs rt<<1|1
#define LL long long
#define MAXN 20000009
int n,m,k;
int tot;
int rtpos[MS];
struct node{
int l,r;
int val;
int size;
}p[MS*30];
void push_up(int rt){
p[rt].size = p[p[rt].l].size + p[p[rt].r].size;
p[rt].val = p[p[rt].l].val + p[p[rt].r].val;
}
int build(int l,int r){
int rt = ++tot;
if(l == r){
return rt;
}
int m = l+r>>1;
p[rt].l = build(l,m);
p[rt].r = build(m+1,r);
return rt;
}
int update(int lart,int l,int r,int pos){
int rt = ++tot;
p[rt] = p[lart];
if(l == r && l == pos){
p[rt].size++;
p[rt].val += pos;
return rt;
}
int m = l+r>>1;
if(m >= pos) p[rt].l = update(p[lart].l,l,m,pos);
else p[rt].r = update(p[lart].r,m+1,r,pos);
push_up(rt);
return rt;
}
int getCnt(int L,int R,int l,int r,int tar){
if(l == r){
if(tar > 0) return (tar-1)/l+1;
else return 0;
}
int x = p[p[R].r].val - p[p[L].r].val;
int y = p[p[R].r].size - p[p[L].r].size;
int m = l+r>>1;
if(x >= tar) return getCnt(p[L].r,p[R].r,m+1,r,tar);
else return y + getCnt(p[L].l,p[R].l,l,m,tar-x);
}
void solve1(){
rtpos[0] = build(1,1000);
for(int i=1;i<=m;i++){
int x;
cin >> x;
rtpos[i] = update(rtpos[i-1],1,1000,x);
}
while(k--){
int L,R;
int tar;
cin >> L >> L >> R >> R >> tar;
L = rtpos[L-1];
R = rtpos[R];
if(p[R].val - p[L].val < tar) cout << "Poor QLW\n";
else cout << getCnt(L,R,1,1000,tar) << "\n";
}
}
int a[209][209];
int val[209][209][1009];
int size[209][209][1009];
void solve2(){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin >> a[i][j];
}
}
for(int o=1;o<=1000;o++){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
val[i][j][o] = val[i-1][j][o]+val[i][j-1][o]-val[i-1][j-1][o];
val[i][j][o] += (a[i][j] >= o? a[i][j] : 0);
size[i][j][o] = size[i-1][j][o]+size[i][j-1][o]-size[i-1][j-1][o];
size[i][j][o] += (a[i][j] >= o? 1 : 0);
}
}
}
while(k--){
int l1,r1,l2,r2;
int tar;
cin >> l1 >> r1 >> l2 >> r2 >> tar;
int sumlr = val[l2][r2][1]-val[l1-1][r2][1]-val[l2][r1-1][1]+val[l1-1][r1-1][1];
if(sumlr < tar){
cout << "Poor QLW\n";
continue;
}
int l=1,r=1001;
int ansval,anssize;
int sumval;
while(l <= r){
int mid = l+r>>1;
int x = val[l2][r2][mid]-val[l1-1][r2][mid]-val[l2][r1-1][mid]+val[l1-1][r1-1][mid];
int y = size[l2][r2][mid]-size[l1-1][r2][mid]-size[l2][r1-1][mid]+size[l1-1][r1-1][mid];
if(x >= tar) l = mid+1;
else{
sumval = x;
ansval = mid;
anssize = y;
r = mid-1;
}
}
anssize += (tar-sumval-1)/(ansval-1)+1;
cout << anssize << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin >> n >> m >> k;
if(n == 1) solve1();
else solve2();
return 0;
}