洛谷题目链接:[国家集训队]矩阵乘法

题目背景

原 《补丁VS错误》请前往P2761

题目描述

给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数。

输入输出格式

输入格式:

第一行两个数N,Q,表示矩阵大小和询问组数;

接下来N行N列一共N*N个数,表示这个矩阵;

再接下来Q行每行5个数描述一个询问:x1,y1,x2,y2,k表示找到以(x1,y1)为左上角、以(x2,y2)为右下角的子矩形中的第K小数。

输出格式:

对于每组询问输出第K小的数。

输入输出样例

输入样例#1:

2 2

2 1

3 4

1 2 1 2 1

1 1 2 2 3

输出样例#1:

1

3

说明

矩阵中数字是10^9以内的非负整数;

20%的数据:N<=100,Q<=1000;

40%的数据:N<=300,Q<=10000;

60%的数据:N<=400,Q<=30000;

100%的数据:N<=500,Q<=60000。


一句话题意: 给出一个\(n*n\)的矩阵,\(q\)次询问,每次查询从\((x1, y1)\)到\((x2, y2)\)的第\(k\)小.


题解: 我做的第一道整体二分的题目,不得不说整体二分真是个好东西.

首先我们来考虑如果只有一次操作应该怎么做.显然可以直接将我们查询的这个矩阵中的所有值排个序,第\(k\)个就是我们要的答案.

虽然这样是没有问题的,但是这样一次操作只能对一个询问有帮助,也就是说,如果询问的区间不同,这个方法的复杂度就会变成\(O(q*n^2*log_2n)\)的.

其实我们还可以用树状数组来代替这个求第\(k\)大的过程.因为树状数组是支持求排名的,所以我们可以二分一个权值,然后将比这个权值小的值都加入树状数组,每次\(check\)就判断在这个区间内的和,也就是求出了某个权值在这个区间中的排名.这样的话复杂度是\(O(n*log_2S*log_2n)\)的,其中\(S\)是权值的范围.

然而上面这个做法对于多组询问就行不通了.这个时候我们需要引入一个神奇的东西: 整体二分.

对于一个询问中的二分的一个权值我们对小于这个权值的数都加入了树状数组中,那么同时其他的询问也可以通过这个修改情况下的树状数组来判断当前二分的权值是否是这组询问的区间第\(k\)小.

这样我们在二分得到一个权值的时候,就能对所有的询问进行判断了.但是有一些询问中的第\(k\)小比这个权值大,而另一些又比这个小.这时候就需要递归处理了.我们可以把询问的第\(k\)小比当前二分权值小的放到左边,比当前二分权值大的放到右边,然后再递归计算左右两边的结果.

还有一点,其实可以不用二分值域,可以现将所有值排个序,然后二分权值在数组中的位置,因为第\(k\)小一定是在所有权值中的.

还没懂可以看一下代码注释.

#include<bits/stdc++.h>
using namespace std;
const int N = 500+5;
const int MAXQ = 60000+5; int n, q, c[N][N], size[MAXQ], ans[MAXQ], id[MAXQ], t1[MAXQ], t2[MAXQ], t[MAXQ], cnt = 0; struct matrix{
int x, y, val;
}a[N*N]; bool cmp(matrix c1, matrix c2){ return c1.val < c2.val; } struct Query{
int x1, y1, x2, y2, k;
}b[MAXQ]; int lowbit(int x){ return x&-x; } void update(int x, int y, int val){
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=n;j+=lowbit(j)) c[i][j] += val;
} int query(int x, int y){
int res = 0;
for(int i=x;i;i-=lowbit(i))
for(int j=y;j;j-=lowbit(j)) res += c[i][j];
return res;
} int calc(int x1, int y1, int x2, int y2){
return query(x2, y2)-query(x2, y1-1)-query(x1-1, y2)+query(x1-1, y1-1);
} void solve(int l, int r, int ql, int qr){
// [l, r]表示第l小到第r小的权值的点(在矩阵中的)
// [ql, qr]表示询问的下标(其实每个询问对应原来的顺序是id[ql~qr]
if(ql > qr) return;//当前递归权值中没有询问则返回
if(l == r){//找到了一个确定的权值就将这些询问的答案赋值
for(int i=ql;i<=qr;i++) ans[id[i]] = a[l].val;
return;
}
int mid = (l+r>>1), cnt1 = 0, cnt2 = 0, sum;
for(int i=l;i<=mid;i++) update(a[i].x, a[i].y, 1);//将小于mid的权值都加入树状数组
//这里循环只从l开始是因为l之前的贡献都已经计入了size数组
//这样可以避免一些重复运算
for(int i=ql;i<=qr;i++){
sum = calc(b[id[i]].x1, b[id[i]].y1, b[id[i]].x2, b[id[i]].y2);
//calc是计算一个区间的和
if(size[id[i]]+sum >= b[id[i]].k) t1[++cnt1] = id[i];
else size[id[i]] += sum, t2[++cnt2] = id[i];
}
for(int i=l;i<=mid;i++) update(a[i].x, a[i].y, -1);
int qcnt = ql-1;
for(int i=1;i<=cnt1;i++) t[++qcnt] = t1[i], id[qcnt] = t[qcnt];
for(int i=1;i<=cnt2;i++) t[++qcnt] = t2[i], id[qcnt] = t[qcnt];
//因为在处理过程中询问的位置关系可能会变,所以用id数组来记录某下标所对应的询问
solve(l, mid, ql, ql+cnt1-1), solve(mid+1, r, ql+cnt1, qr);
//递归处理两边权值的答案
} int main(){
// freopen("data.in", "r", stdin);
ios::sync_with_stdio(false);
int x; cin >> n >> q;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) cin >> x, a[++cnt] = (matrix){ i, j, x };
sort(a+1, a+cnt+1, cmp);
for(int i=1;i<=q;i++) cin >> b[i].x1 >> b[i].y1 >> b[i].x2 >> b[i].y2 >> b[i].k, id[i] = i;
solve(1, cnt, 1, q);
for(int i=1;i<=q;i++) cout << ans[i] << endl;
return 0;
}

[洛谷P1527] [国家集训队]矩阵乘法的更多相关文章

  1. 洛谷 P1527 [国家集训队]矩阵乘法 解题报告

    P1527 [国家集训队]矩阵乘法 题目描述 给你一个\(N*N\)的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第\(K\)小数. 输入输出格式 输入格式: 第一行两个数\(N,Q\),表示矩阵大 ...

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

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

  3. 洛谷$P1527$ [国家集训队]矩阵乘法 整体二分

    正解:整体二分 解题报告: 传送门$QwQ$ 阿看到这种查询若干次第$k$小显然就想到整体二分$QwQ$? 然后现在就只要考虑怎么快速求出一个矩形内所有小于某个数的数的个数? 开始我的想法是离散化然后 ...

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

    \(\color{#0066ff}{ 题目描述 }\) 给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数. \(\color{#0066ff}{输入格式}\) 第一行两个数N,Q ...

  5. P1527 [国家集训队]矩阵乘法 [整体二分]

    权值排序,整体二分,没了. // by Isaunoya #include <bits/stdc++.h> using namespace std; #define rep(i, x, y ...

  6. P1527 [国家集训队]矩阵乘法(整体二分)

    Link 整体二分的经典例题. 对于整体二分,我个人的理解是二分答案套分治. 具体来说就是对答案进行二分,然后对于询问进行类似于权值线段树求区间第 \(k\) 大的分治做法. 首先,我们暴力做法就是对 ...

  7. 【LG1527】[国家集训队]矩阵乘法

    [LG1527][国家集训队]矩阵乘法 题面 洛谷 题解 我也不知道为什么取个这样的名字... 其实就是区间\(kth\)扩展到二维 还是用整体二分搞啦,把树状数组换成二维的 其他的基本没有什么差别 ...

  8. Luogu-1527 [国家集训队]矩阵乘法

    Luogu-1527 [国家集训队]矩阵乘法 题面 Luogu-1527 题解 昨天学CDQ分治时做了一些题,但是因为题(wo)太(tai)水(lan)了(le)并没有整理 学了一晚上的整体二分,拿这 ...

  9. 模板—点分治A(容斥)(洛谷P2634 [国家集训队]聪聪可可)

    洛谷P2634 [国家集训队]聪聪可可 静态点分治 一开始还以为要把分治树建出来……• 树的结构不发生改变,点权边权都不变,那么我们利用刚刚的思路,有两种具体的分治方法.• A:朴素做法,直接找重心, ...

随机推荐

  1. VBA基础之Excel 工作表(Sheet)的操作(二)

    二. Excel 工作表(Sheet)的操作1. Excel 添加工作表(Sheet) 方法名 参数 参数值 说明 Add Before 工作表名称 在指定的工作表前面插入新的工作表 After 工作 ...

  2. Android 网络编程 API笔记 - java.net 包 权限 地址 套接字 相关类 简介

    Android 网络编程相关的包 : 9 包, 20 接口, 103 类, 6 枚举, 14异常; -- Java包 : java.net 包 (6接口, 34类, 2枚举, 12异常); -- An ...

  3. Alpha冲刺——第二天

    Alpha第二天 听说 031502543 周龙荣(队长) 031502615 李家鹏 031502632 伍晨薇 031502637 张柽 031502639 郑秦 1.前言 任务分配是VV.ZQ. ...

  4. Python实现FTP服务功能

    本文从以下三个方面, 阐述Python如何搭建FTP服务器 一. Python搭建FTP服务器 二. FTP函数释义 三. 查看目录结构 四. 上传下载程序 一. Python搭建FTP服务器 1. ...

  5. C#中pictureBox笔记

    if (File.Exists(productInfo.预览图路径)) this.picPreview.Image = BitmapFactory.Alloc(productInfo.预览图路径, f ...

  6. c++ int 负数 补码 隐式类型转换

    unsigned y = ; ; cout << x + y << endl; 对于上述的结果为 这里面有一个负数的补码问题和不同类型之间的隐式类型转换问题 首先负数的表示方法 ...

  7. Redis 学习之主从复制

    该文使用centos6.5 64位    redis3.2.8 主从复制 Redis的复制功能是支持多个数据库之间的数据同步.一类是主数据库(master)一类是从数据库(slave),主数据库可以进 ...

  8. Maven 生命周期 和插件

    1.3 生命周期1.3.1 什么是生命周期? Maven生命周期就是为了对所有的构建过程进行抽象和统一.包括项目清理.初始化.编译.打包.测试.部署等几乎所有构建步骤. 生命周期可以理解为构建工程的步 ...

  9. [C/C++] 友元函数和友元类

    A---友元函数: class Data{ public: ... friend int f(int &m);//友元函数 ... } 友元函数是可以直接访问类的私有成员的非成员函数.它是定义 ...

  10. 【转载】input只改变光标的颜色 不改变字的颜色

    转载 http://www.cnblogs.com/yangAL/p/6934608.html color: red; text-shadow: 0px 0px 0px #000; -webkit-t ...