BZOJ 1926: [Sdoi2010]粟粟的书架(主席树,二分答案)
题意
给你一个长为\(R\)宽为\(C\)的矩阵,第\(i\)行\(j\)列的数为\(P_{i,j}\).
有\(m\)次询问,每次有5个参数\(x_l,x_r,y_l,y_r,h\). 求以\((x_l,y_l)\)为左上角和\((x_r,y_r)\)为右下角的矩形中,至少要选几个值,使得它们的和\(\geq h\).
数据范围
对于\(50 \%\)的数据,满足\(R,C \le 200,M \le 200,000\)
另有\(50 \%\)的数据,满足\(R=1,C \le 500,000,M \le 20,000\)
对于\(100 \%\)的数据,满足\(1 \le P_{i,j} \le 1,000, 1 \le h \le 2,000,000,000\)
题解
这道题很有意思2333
这个题相当于二合一吧,两种不同的方法解决它的子问题(但殊途同归).
第一个\(R,C \le 200,M \le 200,000\).
这个是有点类似于暴力的做法,首先预处理出每种数字出现次数的关于位置和数字大小前缀和,以及出现数字和关于位置数字大小的前缀和.
这个有点绕,直接举例子吧.比如我程序中的
Sum[i][j][k]和Tot[i][j][k].Sum[i][j][k]就是以\((1,1)\)为左上角\((i,j)\)为右下角矩形的,数字\(\ge k\)的和.Tot[i][j][k]就是以\((1,1)\)为左上角\((i,j)\)为右下角矩形的,数字\(\ge k\)的出现次数的和.然后每次就可以二分你需要选的最小的数字了,每次判断可行就直接前缀和容斥就行了.
然后这个选的最小数字不一定要选满,要最后算一下这个数字要选多少个.
令\(n=max(P_{i,j})\)时间复杂度\(O(nRC+m \log n)\),空间复杂度\(O(nRC)\).
第二个\(R=1,C \le 500,000,M \le 20,000\).
这个就是一个序列操作了,这个我认为是这道题的精髓.
我们沿用前一个算法的思想,也是要处理序列上那两个东西.
但时间复杂度肯定要进行优化. 所以有一个数据结构可以支持这个操作,就是主席树!
主席树不仅支持查找区间第\(k\)大,而且还能支持查找区间在\([l,r]\)中数字的和 和 出现数字的和!(看来我数据结构学的真的蠢啊) 以后出现这种题要往这上面想想了.
然后我们可以每次算答案的时候直接在主席树上二分就行了和刚才的操作差不多吧,也类似于查找第\(k\)大.
时间复杂度\(O(C \log n + m \log n)\),空间复杂度\(O(C \log n)\).
代码
/**************************************************************
Problem: 1926
User: zjp_shadow
Language: C++
Result: Accepted
Time:4688 ms
Memory:509916 kb
****************************************************************/
#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), _end_ = (int)(r); i <= _end_; ++i)
#define Fordown(i, r, l) for(register int i = (r), _end_ = (int)(l); i >= _end_; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
inline int read() {
int x = 0, fh = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar() ) if (ch == '-') fh = -1;
for (; isdigit(ch); ch = getchar() ) x = (x<<1) + (x<<3) + (ch ^ '0');
return x * fh;
}
void File() {
#ifdef zjp_shadow
freopen ("P1926.in", "r", stdin);
freopen ("P1926.out", "w", stdout);
#endif
}
const int N = 5e5 + 1e3;
const int maxnode = N * 20;
int take;
inline int Updiv(int a, int b) { return (a / b) + (a % b ? 1 : 0); }
struct ChairMan_Tree {
int T[N], sumv[maxnode], tot[maxnode], lc[maxnode], rc[maxnode], Size;
ChairMan_Tree () { Size = 0; }
void Update(int &o, int pre, int l, int r, int up) {
o = ++Size; lc[o] = lc[pre]; rc[o] = rc[pre];
sumv[o] = sumv[pre] + up; tot[o] = tot[pre] + 1;
if (l == r) return ;
int mid = (l + r) >> 1;
if (up <= mid) Update(lc[o], lc[pre], l, mid, up);
else Update(rc[o], rc[pre], mid + 1, r, up);
}
void Query(int s, int t, int l, int r, int val) {
if (l == r) { take += Updiv(val, l); return ; }
int here = tot[rc[t]] - tot[rc[s]], sv = sumv[rc[t]] - sumv[rc[s]], mid = (l + r) >> 1;
if (val > sv) { take += here; Query(lc[s], lc[t], l, mid, val - sv); }
else Query(rc[s], rc[t], mid + 1, r, val);
}
} CT;
int r, c, m;
int Mat[210][210];
int Sum[210][210][1010];
int Tot[210][210][1010];
int sum[N];
void Solve2() {
For (i, 1, c) {
int val = read();
sum[i] = sum[i - 1] + val;
CT.Update(CT.T[i], CT.T[i - 1], 1, 1000, val);
}
For (i, 1, m) {
read(); int l = read(); read(); int r = read(), h = read();
// cout << "Ask: " << l << ' ' << r << ' ' << h << endl;
take = 0;
if (sum[r] - sum[l - 1] < h) { printf ("Poor QLW\n"); continue ; }
CT.Query(CT.T[l - 1], CT.T[r], 1, 1000, h);
printf ("%d\n", take);
}
}
inline int Calc_Sum (int xl, int yl, int xr, int yr, int low) {
return Sum[xr][yr][low] - Sum[xl - 1][yr][low] - Sum[xr][yl - 1][low] + Sum[xl - 1][yl - 1][low];
}
inline int Calc_Tot (int xl, int yl, int xr, int yr, int low) {
return Tot[xr][yr][low] - Tot[xl - 1][yr][low] - Tot[xr][yl - 1][low] + Tot[xl - 1][yl - 1][low];
}
void Solve1() {
For (i, 1, r)
For (j, 1, c) {
Mat[i][j] = read(); Sum[i][j][Mat[i][j]] += Mat[i][j]; ++Tot[i][j][Mat[i][j]];
For (k, 1, 1000) {
Sum[i][j][k] += Sum[i][j - 1][k] + Sum[i - 1][j][k] - Sum[i - 1][j - 1][k];
Tot[i][j][k] += Tot[i][j - 1][k] + Tot[i - 1][j][k] - Tot[i - 1][j - 1][k];
}
}
For (i, 1, r)
For (j, 1, c)
Fordown (k, 1000, 1) {
Sum[i][j][k] += Sum[i][j][k + 1];
Tot[i][j][k] += Tot[i][j][k + 1];
}
For (i, 1, m) {
int xi = read(), yi = read(), xj = read(), yj = read(), h = read();
int l = 1, r = 1000, need = -1;
while (l <= r) {
int mid = (l + r) >> 1;
if (Calc_Sum(xi, yi, xj, yj, mid) >= h) need = mid, l = mid + 1;
else r = mid - 1;
}
if (need == -1) { printf ("Poor QLW\n"); continue ; }
int Btot = Calc_Tot(xi, yi, xj, yj, need + 1),
Bsum = Calc_Sum(xi, yi, xj, yj, need + 1);
Btot += Updiv(h - Bsum, need);
printf ("%d\n", Btot);
}
}
int main () {
File();
r = read(); c = read(); m = read();
if (r == 1) Solve2(); else Solve1();
return 0;
}
BZOJ 1926: [Sdoi2010]粟粟的书架(主席树,二分答案)的更多相关文章
- bzoj 1926: [Sdoi2010]粟粟的书架 (主席树+二分)
链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1926 题面; 1926: [Sdoi2010]粟粟的书架 Time Limit: 30 Se ...
- BZOJ 4556 [Tjoi2016&Heoi2016]字符串 ——后缀数组 ST表 主席树 二分答案
Solution 1: 后缀数组暴力大法好 #include <map> #include <cmath> #include <queue> #include &l ...
- BZOJ 2653: middle(主席树+二分答案)
传送门 解题思路 首先可以想到一种暴力做法,就是询问时二分,然后大于等于这个值的设为1,否则设为-1,然后就和GSS1那样统计答案.但是发现这样时间空间复杂度都很爆炸,所以考虑预处理,可以用主席树来做 ...
- BZOJ5343[Ctsc2018]混合果汁——主席树+二分答案
题目链接: CTSC2018混合果汁 显然如果美味度高的合法那么美味度低的一定合法,因为美味度低的可选方案包含美味度高的可选方案. 那么我们二分一个美味度作为答案然后考虑如何验证? 选择时显然要贪心的 ...
- 【bzoj2653】【middle】【主席树+二分答案】
Description 一个长度为 n 的序列 a ,设其排过序之后为 b ,其中位数定义为 b[n/2] ,其中 a,b 从 0 开始标号 , 除法取下整. 给你一个长度为 n 的序列 s .回答 ...
- P4094 [HEOI2016/TJOI2016]字符串 后缀数组+主席树+二分答案
$ \color{#0066ff}{ 题目描述 }$ 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为n的字符串s,和m个问题.佳媛姐姐必须 ...
- HDU - 6621 K-th Closest Distance 主席树+二分答案
K-th Closest Distance 主席树第二波~ 题意 给你\(n\)个数\(m\)个询问,问\(i\in [l,r]\)计算每一个\(|a_{i}-p|\)求出第\(k\)小 题目要求强制 ...
- BZOJ.1926.[SDOI2010]粟粟的书架(前缀和 主席树 二分)
题目链接 题意: 在给定矩形区域内找出最少的数,满足和>=k.输出数的个数.两种数据范围. 0~50 注意到(真没注意到...)P[i,j]<=1000,我们可以利用前缀和预处理. num ...
- 2018湘潭邀请赛C题(主席树+二分)
题目地址:https://www.icpc.camp/contests/6CP5W4knRaIRgU 比赛的时候知道这题是用主席树+二分,可是当时没有学主席树,就连有模板都不敢套,因为代码实在是太长了 ...
随机推荐
- (二)异步方法BeginInvoke和EndInvoke
.Net framework可以让你异步调用任何方法,你可以定义一个与你要调用的方法的签名相同的委托.公共语言运行时将自动为该委托定义与签名相同的BeginInvok和EndInvoke方法. Beg ...
- ps色彩范围抠章
1.在photoshop打开需要抠公章的图像文件2.使用椭圆形选区工具,ALT+SHIFT+鼠标左键,选择章的范围 2.CTRL+J,复制图层,得到图层1,隐藏背景 3.CTRL+L,调整色阶,拖动两 ...
- [记录]一则清理MySQL大表以释放磁盘空间的案例
一则清理MySQL大表以释放磁盘空间的案例 一.基本情况: 1.dbtest库554G,先清理st_online_time_away_ds(37G)表的数据,保留半年的数据: 1)删除的数据:sele ...
- nginx虚拟域名的配置以及测试验证
1.保证该机器上安装了nginx 未安装请看:centos/linux下的安装Nginx 2.使用root用户编辑配置文件 vim /usr/local/nginx/conf/nginx.conf 3 ...
- 如何使用supervisor管理你的应用
1.前言 Supervisor(http://supervisord.org/)是用Python开发的一个client/server服务,是UNIX-like系统下的一个进程管理工具,不支持Windo ...
- 王垠:我和Google的故事
也许有人看见过我批判 Google 的那篇英文文章.它好像有一部分片面性,所以被我从英文博客上拿下来了.我一直在反思自己在 Google 的经历,因为在这个公司工作总是感觉不对劲,但是却总也说不清楚为 ...
- 生成1~n的排列
直接递归打印. 代码如下 #include<cstdio> void dfs(int *a,int cur,int n) { if(cur==n) { for(int i=0;i<n ...
- Redis--配置密码
可以通过以下方法进行密码的配置: ① 修改配置文件设置密码 ② 通过命令修改密码(重启redis后,新设置的密码会失效) 此处介绍第一种 1. 找到redis的配置文件,一般在/etc/redis.c ...
- 排序算法整理(python version)
import random import time def bubble_sort(a): n=len(a) while n>1: for i in range(n-1): if a[i]> ...
- linux驱动---字符设备的注册register_chrdev说起
首先我们在注册函数里面调用了register_chrdev(MEM_MAJOR,"mem",&memory_fops),向内核注册了一个字符设备. 第一个参数是主设备号,0 ...