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 比赛的时候知道这题是用主席树+二分,可是当时没有学主席树,就连有模板都不敢套,因为代码实在是太长了 ...
随机推荐
- 【Tools】Pycharm2017 windows安装与修改中文界面教程
[windows] 1.到官网下载Pycharm最新版 https://www.jetbrains.com/pycharm/download/#section=windows 2.安装激活 Pycha ...
- 恢复linux系统文件夹颜色
/etc/DIR_COLORS 默认值 # Background color codes:# 40=black 41=red 42=green 43=yellow 44=blue 45=magenta ...
- PHPUnit 手册
PHPUnit 手册 Sebastian Bergmann 版权 © 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 ...
- 图论算法-Tarjan模板 【缩点;割顶;双连通分量】
图论算法-Tarjan模板 [缩点:割顶:双连通分量] 为小伙伴们总结的Tarjan三大算法 Tarjan缩点(求强连通分量) int n; int low[100010],dfn[100010]; ...
- SDP(8):文本式数据库-MongoDB-Scala基本操作
MongoDB是一种文本式数据库.与传统的关系式数据库最大不同是MongoDB没有标准的格式要求,即没有schema,合适高效处理当今由互联网+商业产生的多元多态数据.MongoDB也是一种分布式数据 ...
- ajax上传图片chrome报错net::ERR_CONNECTION_RESET/net::ERR_CONNECTION_ABORTED
网上搜了一下,base64图片太大,tomcat对post请求大小有默认限制,要在tomcat配置文件server.xml 加一个:maxPostSize="0",0表示无限制 & ...
- 学习Yii框架,有哪些比较好的网站
1.http://www.yiiframework.com/ 2.http://www.kancloud.cn/curder/yii/247741 3.http://www.manks.top/yii ...
- MysqL_SELECT FOR UPDATE详解
先来举一个在某些应用场景下会出现数据不一致的例子,当然存储引擎是InnoDB(至于为什么,后面再告诉你). 电商平台常见的下单场景: 一般商品表(goods)有基本的四个字段,id(主键),goods ...
- Git (gnome-ssh-askpass:3871): Gtk-WARNING **: cannot open display:
在使用Git在客户端使用git push 命令提交文件到github时,出现报错 (gnome-ssh-askpass:): Gtk-WARNING **: cannot open display: ...
- Java数字签名——ECDSA算法
ECDSA 例如微软产品的序列号的验证算法. Elliptic Curve Digital Signature Algorithm,椭圆曲线数字签名算法. 速度快,强度高,签名短 —————————— ...