[洛谷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:朴素做法,直接找重心, ...
随机推荐
- js单行写一个评级组件
单行写一个评级组件:"★★★★★☆☆☆☆☆".slice(5 - rate, 10 - rate); -----------------------------------分隔符- ...
- js实现倒计时60秒的简单代码
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Conten ...
- Linux服务器记录并查询历史操作记录
Linux服务器在使用过程中,经常会有除自己之外的其他人员使用.并不是每个人都对Linux服务器特别熟悉,难免会有一些操作导致服务器报错. 因此,监控Linux服务器的操作并记录下来,是非常有必要的! ...
- AMH面板命令操作大全
LNMP面板 - AMH 命令使用nginx篇 » SSH Nginx1) 有步骤提示操作: ssh执行命令: amh nginx然后选择对应选项进行操作. 2) 或直接操作: 启动Nginx: am ...
- 2018年小米高级 PHP 工程师面试题(模拟考试卷)
1.通过哪一个函数,可以把错误转换为异常处理? A:set_error_handler B:error_reporting C:error2exception D:catch 正确答案:A 答案分析: ...
- OSI参考模型和TCP/IP参考模型
- Oracle导数据到SQL server的方法总结
通过oracle10g 访问sql server 2008 导数据步骤 最近在项目中遇到要将Oracle数据库的数据导入到SQL server数据库中,解决办法如下: 一.准备工作 配置Oracle ...
- RT-thread内核之事件
一.事件控制块:在include/rtdef.h中 #ifdef RT_USING_EVENT /** * flag defintions in event */ #define RT_EVENT_F ...
- [NOIP2016]愤怒的小鸟 状态压缩dp
题目描述 Kiana最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于(0,0)处,每次Kiana可以用它向第一象限发射一只红色的小鸟,小鸟们的飞行轨迹均为形 ...
- I/O复用----poll
2018-08-01 (星期三)poll(): #include <sys/poll.h> int poll (struct pollfd *fd, unsigned int nfds, ...