题意

给你一个长为\(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]粟粟的书架(主席树,二分答案)的更多相关文章

  1. bzoj 1926: [Sdoi2010]粟粟的书架 (主席树+二分)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1926 题面; 1926: [Sdoi2010]粟粟的书架 Time Limit: 30 Se ...

  2. BZOJ 4556 [Tjoi2016&Heoi2016]字符串 ——后缀数组 ST表 主席树 二分答案

    Solution 1: 后缀数组暴力大法好 #include <map> #include <cmath> #include <queue> #include &l ...

  3. BZOJ 2653: middle(主席树+二分答案)

    传送门 解题思路 首先可以想到一种暴力做法,就是询问时二分,然后大于等于这个值的设为1,否则设为-1,然后就和GSS1那样统计答案.但是发现这样时间空间复杂度都很爆炸,所以考虑预处理,可以用主席树来做 ...

  4. BZOJ5343[Ctsc2018]混合果汁——主席树+二分答案

    题目链接: CTSC2018混合果汁 显然如果美味度高的合法那么美味度低的一定合法,因为美味度低的可选方案包含美味度高的可选方案. 那么我们二分一个美味度作为答案然后考虑如何验证? 选择时显然要贪心的 ...

  5. 【bzoj2653】【middle】【主席树+二分答案】

    Description 一个长度为 n 的序列 a ,设其排过序之后为 b ,其中位数定义为 b[n/2] ,其中 a,b 从 0 开始标号 , 除法取下整. 给你一个长度为 n 的序列 s .回答 ...

  6. P4094 [HEOI2016/TJOI2016]字符串 后缀数组+主席树+二分答案

    $ \color{#0066ff}{ 题目描述 }$ 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为n的字符串s,和m个问题.佳媛姐姐必须 ...

  7. HDU - 6621 K-th Closest Distance 主席树+二分答案

    K-th Closest Distance 主席树第二波~ 题意 给你\(n\)个数\(m\)个询问,问\(i\in [l,r]\)计算每一个\(|a_{i}-p|\)求出第\(k\)小 题目要求强制 ...

  8. BZOJ.1926.[SDOI2010]粟粟的书架(前缀和 主席树 二分)

    题目链接 题意: 在给定矩形区域内找出最少的数,满足和>=k.输出数的个数.两种数据范围. 0~50 注意到(真没注意到...)P[i,j]<=1000,我们可以利用前缀和预处理. num ...

  9. 2018湘潭邀请赛C题(主席树+二分)

    题目地址:https://www.icpc.camp/contests/6CP5W4knRaIRgU 比赛的时候知道这题是用主席树+二分,可是当时没有学主席树,就连有模板都不敢套,因为代码实在是太长了 ...

随机推荐

  1. 【linux之shell脚本】

    一.简介 机器语言汇编语言高级语言 面向过程 C Shell Perl 面向对象 java python c++ 强语言:先编译再执行 java c++ 弱语言:边编译边执行 shell python ...

  2. gitlab手动安装

    [博客园 淡水的天空]] 老版 新版 Omnibus package installation Manually

  3. scala学习

    前言 Bruce Eckel在吐槽Java,一是本身的不合理的地方太多,二是Oracle的商业目的导致Java的发布显得有点仓促,许多地方存在不合理,这样便加速了Java的不合理.此外,Bruce提到 ...

  4. golang fmt.printf()

      package main import "fmt" import "os" type point struct { x, y int } func main ...

  5. Jenkins gitlab vue,angular,react 自动化构建【原】

    大致思路,(本篇主要讲vue ,当然了 angular react 也是一样配置) ,转发请注明原链接,谢谢 :) 1. 服务器上面配置jenkins (安装配置,不介绍) 2.新建item 自由风格 ...

  6. CENTOS6.6 下mysql MHA架构搭建

    本文来自我的github pages博客http://galengao.github.io/ 即www.gaohuirong.cn 摘要: 本篇是自己搭建的一篇mysql MHA文章 前面的安装步骤基 ...

  7. 十年磨一剑 Delphi重新崛起再写传奇

    新年伊始,英巴卡迪诺公司(Embarcadero)就在其官网发布了"激动人心的RAD Studio2018年发展规划"公告(见上图).公告中指出,将在于2018年第一季度发布10. ...

  8. 【深度学习系列】用PaddlePaddle进行车牌识别(一)

    小伙伴们,终于到了实战部分了!今天给大家带来的项目是用PaddlePaddle进行车牌识别.车牌识别其实属于比较常见的图像识别的项目了,目前也属于比较成熟的应用,大多数老牌厂家能做到准确率99%+.传 ...

  9. Mybatis使用总结-思维导图

    目前使用到Mybatis的总结,缓存还未涉及:

  10. centos6.9 开机进入grub界面问题解决

    安装系统时没把之前的磁盘分区格式化,导致安装新系统grub冲突 解决办法:删除当前磁盘分区,重新安装系统