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

题目背景

原 《补丁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. HDU 4115 Eliminate the Conflict(2-SAT)(2011 Asia ChengDu Regional Contest)

    Problem Description Conflicts are everywhere in the world, from the young to the elderly, from famil ...

  2. Android中的回调Callback

    回调就是外部设置一个方法给一个对象, 这个对象可以执行外部设置的方法, 通常这个方法是定义在接口中的抽象方法, 外部设置的时候直接设置这个接口对象即可. 例如给安卓添加按钮点击事件, 我们创建了OnC ...

  3. 生成以指定字符为开头的md5值(6位数字)

    以下脚本的功能是生成以指定字符为开头的md5值 #-*- coding:utf-8 -*- #脚本功能:生成以指定字符为开头的md5值(6位数字) import hashlib import rand ...

  4. mysql向上递归&向下递归

    工作记录 向上递归函数test: BEGIN ); ); SET sTemp = '$'; SET sTempChd =cast(rid as CHAR); WHILE sTempChd is not ...

  5. 学习 SQL 语句 - Select(9): 其他

    //只要前五条记录 procedure TForm1.Button1Click(Sender: TObject); begin   with ADODataSet1 do begin     Clos ...

  6. IO复用、多进程和多线程三种并发编程模型

    I/O复用模型 I/O复用原理:让应用程序可以同时对多个I/O端口进行监控以判断其上的操作是否可以进行,达到时间复用的目的.在书上看到一个例子来解释I/O的原理,我觉得很形象,如果用监控来自10根不同 ...

  7. CS6的安装与破解

    大家在Mac下肯定也少不了对图片进行修改,那也就少不了Photoshop这款软件. 今天在这里分享下苹果下的Adobe PhotoshopCS6,这个软件大家应该都很熟悉,主要功能什么我就不多做介绍了 ...

  8. 【bzoj2699】更新 dp

    题目描述 对于一个数列A[1..N],一种寻找最大值的方法是:依次枚举A[2]到A[N],如果A[i]比当前的A[1]值要大,那么就令A[1]=A[i],最后A[1]为所求最大值.假设所有数都在范围[ ...

  9. POJ3422:Kaka's Matrix Travels——题解

    http://poj.org/problem?id=3422 题目大意: 从左上角走到右下角,中途取数(数>=0),然后该点的数变为0,求走k的总价值和最大值. ———————————————— ...

  10. hdu6184 Counting Stars 【三元环计数】

    题目链接 hdu6184 题解 题意是让我们找出所有的这样的图形: 我们只需要求出每条边分别在多少个三元环中,记为\(x\),再然后以该点为中心的图形数就是\({x \choose 2}\) 所以我们 ...