[洛谷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:朴素做法,直接找重心, ...
随机推荐
- android4.3 Bluetooth分析之扫描分析
android4.3中引入了蓝牙低能耗le(low energy),相应的也有一些方法/类.不过代码里,并没有找到初始调用的地方.所以这里还是先只分析下bt普通的扫描流程(类似android 4.2) ...
- c# byte[] 保存图片
1.用函数即可,File.WriteAllBytes(@"E:\123.bmp", pcBMPBuffer); 2.byte[]也可和image互相转化.
- 寒假学习计划——MOOC
课程 西安交通大学[https://www.icourse163.org/course/XJTU-46006?tid=1002265006] 理由 本身中国大学mooc里c++课程不多,完结了能够有很 ...
- iOS- NSThread/NSOperation/GCD 三种多线程技术的对比及实现
1.iOS的三种多线程技术 1.NSThread 每个NSThread对象对应一个线程,量级较轻(真正的多线程) 2.以下两点是苹果专门开发的“并发”技术,使得程序员可以不再去关心线程的具体使用问题 ...
- 从大量的IP访问记录中找到访问次数最多的IP
1.内存不受限 一个IP有32bit(4Byte),1GB=10亿,那么在4GB内存的情况下,可以存10亿个IP.用HashMap,边存入IP边维护一个最大次数,这样遍历一遍就可以求出,时间复杂度为O ...
- Android------BottonTabBar
前言:一款简单好用封装好的AndroidUI控件,底部导航栏. 1.使用 1.1添加 compile 'com.hjm:BottomTabBar:1.1.1' 1.2 activity_main. ...
- Winform程序部署方式总结一——ClickOnce发布
针对Winform程序,介绍两种常用打包方式:ClickOnce和Windows Installer 应用程序如下: 一.ClickOnce发布 1.找到需要发布的项目文件,右击,从弹出的快捷菜单中找 ...
- BZOJ 1015 星球大战(并查集)
正着不好搞,考虑倒着搞.倒着搞就是一个并查集. # include <cstdio> # include <cstring> # include <cstdlib> ...
- 【bzoj4736/uoj#274】[清华集训2016]温暖会指引我们前行 语文题+LCT
题目描述 http://uoj.ac/problem/274 题解 语文题+LCT 对于这种语文题建议还是自己读题好一些... 读懂题后发现:由于温度互不相同,最大生成树上的路径必须走(不走的话温度大 ...
- android应用打前需要准备些啥?
发布之前我们需要准备的东西 参考了google官方和结合国内开发经验 1) 用户协议(本地.网络.API) 2) 签名文件(用于为APP加密,唯一标签) 3) 程序图标(第一个面对用户,准备不同的但合 ...