[洛谷P1527] [国家集训队]矩阵乘法
洛谷题目链接:[国家集训队]矩阵乘法
题目背景
原 《补丁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] [国家集训队]矩阵乘法的更多相关文章
- 洛谷 P1527 [国家集训队]矩阵乘法 解题报告
P1527 [国家集训队]矩阵乘法 题目描述 给你一个\(N*N\)的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第\(K\)小数. 输入输出格式 输入格式: 第一行两个数\(N,Q\),表示矩阵大 ...
- 洛谷P1527 [国家集训队] 矩阵乘法 [整体二分,二维树状数组]
题目传送门 矩阵乘法 题目描述 给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数. 输入输出格式 输入格式: 第一行两个数N,Q,表示矩阵大小和询问组数: 接下来N行N列一共N* ...
- 洛谷$P1527$ [国家集训队]矩阵乘法 整体二分
正解:整体二分 解题报告: 传送门$QwQ$ 阿看到这种查询若干次第$k$小显然就想到整体二分$QwQ$? 然后现在就只要考虑怎么快速求出一个矩形内所有小于某个数的数的个数? 开始我的想法是离散化然后 ...
- P1527 [国家集训队]矩阵乘法
\(\color{#0066ff}{ 题目描述 }\) 给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数. \(\color{#0066ff}{输入格式}\) 第一行两个数N,Q ...
- P1527 [国家集训队]矩阵乘法 [整体二分]
权值排序,整体二分,没了. // by Isaunoya #include <bits/stdc++.h> using namespace std; #define rep(i, x, y ...
- P1527 [国家集训队]矩阵乘法(整体二分)
Link 整体二分的经典例题. 对于整体二分,我个人的理解是二分答案套分治. 具体来说就是对答案进行二分,然后对于询问进行类似于权值线段树求区间第 \(k\) 大的分治做法. 首先,我们暴力做法就是对 ...
- 【LG1527】[国家集训队]矩阵乘法
[LG1527][国家集训队]矩阵乘法 题面 洛谷 题解 我也不知道为什么取个这样的名字... 其实就是区间\(kth\)扩展到二维 还是用整体二分搞啦,把树状数组换成二维的 其他的基本没有什么差别 ...
- Luogu-1527 [国家集训队]矩阵乘法
Luogu-1527 [国家集训队]矩阵乘法 题面 Luogu-1527 题解 昨天学CDQ分治时做了一些题,但是因为题(wo)太(tai)水(lan)了(le)并没有整理 学了一晚上的整体二分,拿这 ...
- 模板—点分治A(容斥)(洛谷P2634 [国家集训队]聪聪可可)
洛谷P2634 [国家集训队]聪聪可可 静态点分治 一开始还以为要把分治树建出来……• 树的结构不发生改变,点权边权都不变,那么我们利用刚刚的思路,有两种具体的分治方法.• A:朴素做法,直接找重心, ...
随机推荐
- Discover the Web(栈模拟)
Description Standard web browsers contain features to move backward and forward among the pages rece ...
- 【转】自定义(滑动条)input[type="range"]样式
1.如何使用滑动条? 用法很简单,如下所示: <input type="range" value="0"> 各浏览器原始样式如下: Chrome: ...
- Java中I/O流之轮换流
Java 中的轮换流: 非常有用,可以把一个字节流转换成字符流. inputStreamReader, outputStreamReader Demo_1: import java.io.*; cla ...
- udf.dll 源码
一点关于UDF的发散思路 Author:mer4en7y Team:90sec 声明:UDF源码作者langouster 相信各位牛对UDF都不会陌生,看论坛叶总共享了一份UDF源码,以前一直没看过, ...
- web前端之路 - 开篇
一 web发展历程 了解事物的历史有助于我们渐进式的从发展的思路清楚了解事物的来龙去脉. 这里有一篇网文写得比较清晰和完整:https://www.tianmaying.com/tutorial/we ...
- JAVA学习之HashCode
public native int hashCode(); 返回该对象的哈希码值.支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能. 一.HashCode ...
- bzoj1069-最大土地面积
题目 给定\(n\ (n\le 2000)\)个坐标,求四个坐标使得围起来的四边形面积最大. 分析 最暴力的想法是枚举四个点,然而肯定超时.接着不知道怎么想到中途相遇,然而一点关系都没有.这里用到了一 ...
- hdu 1848(Fibonacci again and again)(SG博弈)
Fibonacci again and again Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Jav ...
- Linq里where出现null的问题
今天遇到一个问题,怎么在where里判断一个字段是否为null,并且这个字段不是字符串string类型,而是int和GUID类型,折腾了半天终于搞明白了.(由于项目是我半路接手的,问题是前期的同事给我 ...
- 用css制作空心箭头(上下左右各个方向均有)
平常在网页中,经常会有空心箭头,除了用图片外,可以用css来实现.基本思路是,用css绘制两个三角形,通过绝对定位让两三角形不完全重叠,例如制作向右的空心箭头,位于前面的三角形border颜色是需要的 ...