题意

给你一个长为\(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. sql的升阶

    前言:基本数据库操作根本无法满足实际的需要,需要引入更多的操作. 触发器-隐式的,主动的,更新数据表中的信息.带有inserted和deleted两个临时表,代表新操作和旧操作. 它是一种特殊的存储过 ...

  2. nodejs http post 请求带参数

    // We need this to build our post string var querystring = require('querystring'); var http = requir ...

  3. Invoke 与 BeginInvoke 应用场景

    1.委托中 Invoke , BeginInvoke 特点 Invoke  : 同步调用 , 委托在当前线程执行 BeginInvoke : 异步调用 , 通常使用线程池资源执行委托. 2. UI  ...

  4. 35 个 jQuery 小技巧

    1. 禁止右键点击 $(document).ready(function(){ $(document).bind("contextmenu",function(e){ return ...

  5. volatile简要解析

    在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写.这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值 ...

  6. OSI网络模型

    OSI中的层 功能 TCP/IP协议族 应用层         文件传输,电子邮件,文件服务,虚拟终端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 表示层         数 ...

  7. hdu 2048 递推&&错排

    直接贴出递推公式: cnt[n]=(i-1)*(cnt[n-1]+cnt[n-2]); 数组保存的是失败的种数 AC代码: #include<cstdio> const int maxn= ...

  8. STL中坑爹的max函数

    hdu1754永远难忘的痛,参数最好不要传耗时特别长的函数,因为会调用两次,如果是递归的话,不知道多少次呢.. 切记!切记!切记! 例如: //return max(getAns(root<&l ...

  9. hdu1698 Just a Hook 线段树

    共有Q个更新,每次更新给更新的区间一个标记,表示该区间是在哪一次被更新,最后统计答案是以最近被更新的值为答案. AC代码: #include<cstdio> const int maxn= ...

  10. Spring Boot 整合 Mybatis Annotation 注解的完整 Web 案例

    摘要: 原创出处 www.bysocket.com 「泥瓦匠BYSocket 」欢迎转载,保留摘要,谢谢! 『 公司需要人.产品.业务和方向,方向又要人.产品.业务和方向,方向… 循环』 本文提纲一. ...