BZOJ 2738 子矩阵第k大 | 二维树状数组 整体二分 分治
BZOJ 2738 “矩阵乘法”(子矩阵第k大)
题意
给出一个矩阵,多次询问子矩阵中第k大的数是多少。
题解
我做这道题之前先照着这道题出了一道题,是这道题的一维版本,在这里:https://vijos.org/d/contest/p/5a26541bd3d8a11cef1706aa。
思路是这样的:二分答案mid,将所有小于mid的位置都在树状数组上 +1,对于每个询问,如果子矩阵所有”+1“之和 >= 这个询问的k,则把询问划分到”左边那一组”,否则划分到“右边那一组”,之后对它们分别处理。
核心代码:
void solve(int l, int r, int ql, int qr){
    if(ql > qr) return;
    if(l == r){
	for(int i = ql; i <= qr; i++)
	    ans[q[i].id] = a[l].val;
	return;
    }
    int mid = (l + r) >> 1, cnt = 0;
    for(int i = l; i <= mid; i++)
	add(a[i].x, a[i].y, 1);
    for(int i = ql; i <= qr; i++){
	sum[i] = ask(q[i]);
	if(sum[i] >= q[i].k) cnt++;
    }
    for(int i = ql, pl = ql, pr = ql + cnt; i <= qr; i++)
	if(sum[i] >= q[i].k) tmp[pl++] = q[i];
	else q[i].k -= sum[i], tmp[pr++] = q[i];
    for(int i = ql; i <= qr; i++) q[i] = tmp[i];
    for(int i = l; i <= mid; i++)
	add(a[i].x, a[i].y, -1);
    solve(l, mid, ql, ql + cnt - 1);
    solve(mid + 1, r, ql + cnt, qr);
}
完整代码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef long long ll;
template <class T>
void read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
	if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
	x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x){
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}
const int N = 505, M = 60005;
int n, m, tr[N][N], ans[M], idx, sum[M];
struct element {
    int x, y, val;
    bool operator < (const element &b) const{
	return val < b.val;
    }
} a[N*N];
struct query {
    int id, xa, ya, xb, yb, k;
} q[M], tmp[M];
void add(int x, int y, int val){
    for(int px = x; px <= n; px += px & -px)
	for(int py = y; py <= n; py += py & -py)
	    tr[px][py] += val;
}
int single_ask(int x, int y){
    int ret = 0;
    for(int px = x; px; px -= px & -px)
	for(int py = y; py; py -= py & -py)
	    ret += tr[px][py];
    return ret;
}
int ask(query Q){
    return single_ask(Q.xb, Q.yb) - single_ask(Q.xa - 1, Q.yb) - single_ask(Q.xb, Q.ya - 1) + single_ask(Q.xa - 1, Q.ya - 1);
}
void solve(int l, int r, int ql, int qr){
    if(ql > qr) return;
    if(l == r){
	for(int i = ql; i <= qr; i++)
	    ans[q[i].id] = a[l].val;
	return;
    }
    int mid = (l + r) >> 1, cnt = 0;
    for(int i = l; i <= mid; i++)
	add(a[i].x, a[i].y, 1);
    for(int i = ql; i <= qr; i++){
	sum[i] = ask(q[i]);
	if(sum[i] >= q[i].k) cnt++;
    }
    for(int i = ql, pl = ql, pr = ql + cnt; i <= qr; i++)
	if(sum[i] >= q[i].k) tmp[pl++] = q[i];
	else q[i].k -= sum[i], tmp[pr++] = q[i];
    for(int i = ql; i <= qr; i++) q[i] = tmp[i];
    for(int i = l; i <= mid; i++)
	add(a[i].x, a[i].y, -1);
    solve(l, mid, ql, ql + cnt - 1);
    solve(mid + 1, r, ql + cnt, qr);
}
int main(){
    read(n), read(m);
    for(int i = 1; i <= n; i++)
	for(int j = 1; j <= n; j++)
	    a[++idx].x = i, a[idx].y = j, read(a[idx].val);
    sort(a + 1, a + idx + 1);
    for(int i = 1; i <= m; i++)
	q[i].id = i, read(q[i].xa), read(q[i].ya), read(q[i].xb), read(q[i].yb), read(q[i].k);
    solve(1, idx, 1, m);
    for(int i = 1; i <= m; i++)
	write(ans[i]), enter;
    return 0;
}
BZOJ 2738 子矩阵第k大 | 二维树状数组 整体二分 分治的更多相关文章
- 洛谷1527(bzoj2738)矩阵乘法——二维树状数组+整体二分
		题目:https://www.luogu.org/problemnew/show/P1527 不难想到(?)可以用二维树状数组.但维护什么?怎么查询是难点. 因为求第k小,可以考虑记权值树状数组,把比 ... 
- 洛谷P1527 矩阵乘法——二维树状数组+整体二分
		题目:https://www.luogu.org/problemnew/show/P1527 整体二分,先把所有询问都存下来: 然后二分一个值,小于它的加到二维树状数组的前缀和里,判断一遍所有询问,就 ... 
- BZOJ 1452 Count 【模板】二维树状数组
		对每种颜色开一个二维树状数组 #include<cstdio> #include<algorithm> using namespace std; ; ][maxn][maxn] ... 
- 【 HDU - 4456 】Crowd (二维树状数组、cdq分治)
		BUPT2017 wintertraining(15) #5A HDU 4456 题意 给你一个n行n列的格子,一开始每个格子值都是0.有M个操作,p=1为第一种操作,给格子(x,y)增加z.p=2为 ... 
- BZOJ3110[Zjoi2013]K大数查询(树状数组+整体二分)
		3110 [Zjoi2013]K大数查询 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a ... 
- BZOJ 1452:[JSOI2009]Count(二维树状数组)
		[JSOI2009]Count 描述 输入 输出 1 2 分析: 裸二维bit,对每个颜色建一颗bit. program count; var bit:..,..,..]of longint; a:. ... 
- POJ 1195:Mobile phones 二维树状数组
		Mobile phones Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 16893 Accepted: 7789 De ... 
- BZOJ.2738.矩阵乘法(整体二分 二维树状数组)
		题目链接 BZOJ 洛谷 整体二分.把求序列第K小的树状数组改成二维树状数组就行了. 初始答案区间有点大,离散化一下. 因为这题是一开始给点,之后询问,so可以先处理该区间值在l~mid的修改,再处理 ... 
- BZOJ 2738 矩阵乘法(整体二分+二维树状数组)
		[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2738 [题目大意] 给出一个方格图,询问要求求出矩阵内第k小的元素 [题解] 我们对答 ... 
随机推荐
- Python字符串符号:双引号/单引号用法注解。
			众所周知python中单引号和双引号常常被我们所使用,例如print.input等等. 但是对于打印输出所引导的字符串大多都是用双引号的形式来做,"Hello,python!",而 ... 
- 《数据结构与算法图解》 分享 pdf下载
			链接:https://pan.baidu.com/s/1gOMlwU5ucHYDVazvVMk2uw提取码:bk5x 
- Java生成唯一ID
			这里我用的是Java提供的java.util.UUID类来产生随机字串,UUID码是什么我就不再赘述,能满足我们的需求就可以. 下面是java代码: import java.util.UUID; pu ... 
- 关于MySql数据库主键及索引的区别
			一.什么是索引?索引用来快速地寻找那些具有特定值的记录,所有MySQL索引都以B-树的形式保存.如果没有索引,执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录,直至找到符合要求的记录.表里 ... 
- 学习笔记 | treap | splay
			目录 前言 treap 它的基本操作 前言 不会数据结构选手深深地感受到了来自treap的恶意QwQ 在听的时候感觉自己听得听懂的??大概只是听懂了它的意思 代码是怎么写都感觉写不好╮(╯﹏╰)╭ 菜 ... 
- GO/GOLANG程序员笔记大全
			---------------------------------------- go 并发 // 注解:go 语言天生为程序并发所设计,可以说go的强项就是在cpu并发上的处理. // go 语言层 ... 
- [linux] LVM磁盘管理(针对xfs和ext4不同文件系统)
			简单来说就是:PV:是物理的磁盘分区VG:LVM中的物理的磁盘分区,也就是PV,必须加入VG,可以将VG理解为一个仓库或者是几个大的硬盘LV:也就是从VG中划分的逻辑分区如下图所示PV.VG.LV三者 ... 
- #1490 : Tree Restoration-(微软2017在线笔试)
			输入n m km个数,表示每层的节点个数接下来m行是每层的节点,节点顺序是从左往右的k个叶子节点k*k个矩阵,表示叶子节点之间的距离 输出:每个节点的父亲节点编号,root节点是0 题解:1.很明显, ... 
- JSBridge的原理
			前言 参考来源 前人栽树,后台乘凉,本文参考了以下来源 github-WebViewJavascriptBridge JSBridge-Web与Native交互之iOS篇 Ios Android Hy ... 
- 实验二 Java面向对象程序化设计
			实验二 Java面向对象程序设计 一. 实验要求 1.完成实验.撰写实验报告,以博客方式发表在博客园 2.实验报告重点是运行结果.遇到的问题(工具查找,安装,使用,程序的编辑,调试,运行等).解决办 ... 
