–> 洛谷 P1527 <–

这是一道整体二分的经典题目。

这道题显然可以给每个询问二分答案,统计该询问矩阵中小于等于mid的元素个数。如果大于等于k,说明猜大了,否则说明猜小了。

如果用这种方法的话,对于每个询问都至少要用O(询问矩阵大小*log值域)的时间复杂度解决,多组询问的话时间不能接受。

发现多个询问的二分答案是可以同时被检验的,我们可以为所有询问同时二分答案,把所有答案小于等于mid的询问放在询问序列的左侧,大于mid的放到询问序列的右侧然后递归处理。

这样为什么会快呢?我们每次可以用把矩阵中小于等于mid的元素染成黑色,剩下的元素保持白色。这样对于每一个询问的检验,就相当于是统计某个子矩阵中黑点的个数。为什么不用二维树状数组维护前缀和呢?

最开始的时候整个矩阵都设为0,然后让所有小于等于mid的位置加1,O(N2log2N)O(N2log2⁡N)O(N^2 \log^2 N)处理完整个矩阵之后再O(log2N)O(log2N)O(log^2 N)去应付每一个询问。被询问的子矩阵中可能有很多重叠的部分,这样保证了在每次询问过程中,矩阵中的每个元素只对运行时间做一次贡献,所以这个算法要比对于每个询问单独二分要快得多。

这样做的时间复杂度为什么是对的呢?我们是在二分值域,考虑二分过程中的每一层对答案的贡献。

  1. 对于每一层二分,矩阵中的每个元素最多被加入树状数组一次。

  2. 对于每一层二分,每个询问只会被处理一次。

  3. 二分值域的过程中最多只会出现O(logN)O(log⁡N)O(\log N)层。

时间复杂度O((N2+Q)log3N)O((N2+Q)log3⁡N)O((N^2 + Q)\log^3 N),感觉这时间复杂度不开O2过不了啊,想要不开O2 AC可能得大力卡常一发。

发表一下个人见解:感觉CDQ分治和整体二分有异曲同工之妙,是同一种分治思想在不同维度发挥作用的体现。(之所以这么说是因为代码写起来是非常像的。)

还有一点,不要真的去二分值域,不然还得需要离散化,常数更大。把矩阵中的所有元素按照权值排序,二分在答案在排序之后的数组中的位置即可。(因为答案一定是矩阵中存在的值,所以在这些值里面二分就好了。)

给出代码:

// luogu-judger-enable-o2
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;inline int geti() {int ans = 0; bool flag = 0; char c = getchar();while(!isdigit(c)) flag |= c == '-', c = getchar();while( isdigit(c)) ans=ans*10+c-'0', c = getchar();return flag? -ans: ans;
}inline void puti(int x) {if(x < 0) x=-x, putchar('-');if(x > 9) puti(x / 10);putchar('0' + x%10);
}const int maxn = 500 + 10, maxq = 60000 + 5;struct Numbers {int x, y, v; /// (x,y) 位置的值为 v bool operator < (const Numbers& rhs) const {return v < rhs.v; /// 用于比较大小 }
} Matrix[maxn * maxn]; int mcnt; /// 矩阵中的元素个数struct BIT {int n; /// n * n 的二维树状数组int C[maxn][maxn];BIT(int N = 0) {n = N; memset(C, 0x00, sizeof(C));} /// 初始化inline int lowbit(int x) {return x&(-x);}inline void add(int x, int y, int v) { /// 单点修改 for(register int i = x; i <= n; i += lowbit(i))for(register int j = y; j <= n; j += lowbit(j))C[i][j] += v;}inline int pre(int x, int y) { /// 前缀求和 int ans = 0;for(register int i = x; i > 0; i -= lowbit(i))for(register int j = y; j > 0; j -= lowbit(j))ans += C[i][j];return ans;}inline int submat(int x1, int y1, int x2, int y2) { /// 子矩阵和 int ans = pre(x2, y2);ans -= pre(x1 - 1, y2) + pre(x2, y1 - 1);ans += pre(x1 - 1, y1 - 1);return ans;}
} bit; /// 记得初始化 nstruct Events { /// 记录所有询问 int x1, y1, x2, y2, k;inline void input() { /// 输入一个询问 x1 = geti(); y1 = geti();x2 = geti(); y2 = geti();k  = geti();}
} Querys[maxq];int bcount(Events mat) { /// 查询某个询问中的黑点个数 return bit.submat(mat.x1, mat.y1, mat.x2, mat.y2);
}int id[maxq], t1[maxq], t2[maxq];
int ans[maxq], cur[maxq];void Sol(int l, int r, int ql, int qr) {if(qr < ql) return; /// 该值域区间没有询问if(l == r) {for(int i = ql; i <= qr; i ++) ans[id[i]] = Matrix[l].v;return; /// 找到解 }int mid = (l + r)/2;for(int i = l; i <= mid; i ++) /// 把要统计到答案中的数值染黑 bit.add(Matrix[i].x, Matrix[i].y, 1);int cnt1 = 0, cnt2 = 0;for(int i = ql; i <= qr; i ++) {int u = id[i]; /// 当前要处理的询问int s = cur[u] + bcount(Querys[u]); /// 考虑当前区间中的黑点个数if(s >= Querys[u].k) t1[++ cnt1] = u;else t2[++ cnt2] = u, cur[u] = s; }int qcnt = ql - 1;for(int i = 1; i <= cnt1; i ++) id[++ qcnt] = t1[i]; /// 左右分组 for(int i = 1; i <= cnt2; i ++) id[++ qcnt] = t2[i];for(int i = l; i <= mid; i ++) /// 谁污染谁治理 bit.add(Matrix[i].x, Matrix[i].y, -1);Sol(l, mid, ql, ql + cnt1 - 1);Sol(mid+1, r, ql + cnt1, qr);
}int main() {int N = geti(), Q = geti(); bit.n = N;/// 输入矩阵大小和询问组数for(int i = 1; i <= N; i ++)for(int j = 1; j <= N; j ++)Matrix[++ mcnt] = (Numbers){i, j, geti()};sort(Matrix + 1, Matrix + mcnt + 1); /// 按元素大小从小到大排序for(int i = 1; i <= Q; i ++) Querys[i].input();for(int i = 1; i <= Q; i ++) id[i] = i;Sol(1, mcnt, 1, Q);for(int i = 1; i <= Q; i ++) puti(ans[i]), putchar('\n');return 0;
}

[国家集训队] 矩阵乘法相关推荐

  1. P1527 [国家集训队]矩阵乘法

    P1527 [国家集训队]矩阵乘法 题意: 给你一个 n×n 的矩阵,每次询问一个子矩形的第 k 小数. 题解: 整体二分稍微加强化 模板题是一个序列,现在升级成一个矩阵求,做法和原理都是一样的 使用 ...

  2. 洛谷P1527 [国家集训队] 矩阵乘法 [整体二分,二维树状数组]

    题目传送门 矩阵乘法 题目描述 给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数. 输入输出格式 输入格式: 第一行两个数N,Q,表示矩阵大小和询问组数: 接下来N行N列一共N* ...

  3. P1527 [国家集训队]矩阵乘法 整体二分 + 二维树状数组

    传送门 题意: 思路: 算是个整体二分的板子啦,不过这个是二维的矩阵,我们只需要把一位树状数组改成二维的,让后动态维护单点加,区间查询前缀和即可. //#pragma GCC optimize(2) ...

  4. 【洛谷1527】 [国家集训队]矩阵乘法(整体二分)

    传送门 洛谷 Solution 考虑看到什么k小就整体二分套上去试一下. 矩形k小整体二分+二维树状数组就好了. 代码实现 // luogu-judger-enable-o2 /*mail: mlea ...

  5. Luogu4451 [国家集训队]整数的lqp拆分

    题目链接:洛谷 题目大意:求对于所有$n$的拆分$a_i$,使得$\sum_{i=1}^ma_i=n$,$\prod_{i=1}^mf_{a_i}$之和.其中$f_i$为斐波那契数列的第$i$项. 数 ...

  6. zcmu-1184(矩阵乘法)

    1184: 帮我求算一下斐波那契数吧 Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 221  Solved: 42 [Submit][Status][ ...

  7. 国家集训队论文分类整理[转]

    国家集训队论文分类整理 转自这里 dalao写的东西,是非常有学习价值的.反正noip不一定会用,但是对以后肯定有用的. 组合数学 计数与统计 2001 - 符文杰:<Pólya原理及其应用&g ...

  8. 国家集训队论文分类整理

    国家集训队论文分类整理 组合数学 计数与统计 2001 - 符文杰:<Pólya原理及其应用> 2003 - 许智磊:<浅谈补集转化思想在统计问题中的应用> 2007 - 周冬 ...

  9. 国家集训队论文分类整理(转)

    国家集训队论文分类整理 ----------转自https://www.cnblogs.com/AbandonZHANG/archive/2012/07/21/2601889.html 距离ACM/I ...

  10. 国家集训队论文集题目

    这些论文还是很不错的,可以当成专题到谷歌上搜着看看,权当拓展下知识. 国家集训队1999论文集 陈宏:<数据结构的选择与算法效率--从IOI98试题PICTURE谈起> 来煜坤:<把 ...

最新文章

  1. 【python】一次移动平均算法
  2. 一起做激光SLAM:ICP匹配用于闭环检测
  3. jqm跳转js不加载
  4. “解剖”HIGO徐易容:关于创业、后悔、喝酒和滑雪
  5. 单元格内多个姓名拆分成一列_excel单元格拆分拆分同一单元格中的姓名,原来这么简单啊!...
  6. 回调java 简书_web3j函数回调使用详解
  7. JavaWeb中集成UEditor
  8. Web前端和后端开发的区别和要求
  9. Ubuntu 16.04 LTS误删系统内核或驱动导致无法上网解决方案
  10. MyEclipse7.0及JDK1.6.0的安装及配置过程(修改)
  11. 浏览器工作原理(一):浏览器的进程与线程
  12. Docker-端口映射实现访问容器
  13. 全开源的固定资产设备管理系统,JAVA项目源码
  14. 我的大数据之路 -- 猫眼电影再战
  15. Win10怎么搜索文件内容?Win10通过文件内容查找文件的方法
  16. 使用 Colab 训练 Pytorch-Yolov4 (WongKinYiu版)
  17. 金融分析(三)------联合分布,边缘分布,条件概率密度
  18. Hive架构及相关函数
  19. 软件测试工程师面试的时候该怎么样介绍自己?
  20. html模拟点击某个键盘按钮,如何使用JavaScript模拟按键或单击?

热门文章

  1. 深度学习自学(二十六):人脸关键点检测
  2. 并行网络测试软件,Manul:一款基于覆盖率引导的并行模糊测试工具
  3. 免费真实增加网站访问量的方法
  4. 计算机无法识别打印机usb,Windows7打印机usb无法识别如何解决
  5. 统计分析之:正态性检验——SPSS操作指南
  6. webshell检测方法归纳
  7. android模拟登陆,android,httpclient_Android模拟登录数据获取不了?无法传递?,android,httpclient,模拟登录 - phpStudy...
  8. pycharm中遇到master in has no tracked branch的解决方式
  9. win7系统关闭445端口批处理脚本
  10. layui扩展模块的使用注意事项