BZOJ 3110 K大数查询 | 整体二分
BZOJ 3110 K大数查询
题面
有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c
如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
题解
这道题一个好写的做法是“整体二分”。
我做的上一道整体二分的题目在这里。
整体二分的主要过程是:二分答案,然后按照 答案比当前mid小/答案比当前mid大 将询问分为两组,一组放在左边,另一组放在右边,然后递归进行左右两边的二分。
这道题有修改操作,但是也没什么关系,不要被吓住了 =_=
我们的分治策略保证了当前要处理的的询问在时间上是单调的。于是我们从左到右枚举当前要处理的询问,如果是修改操作,则:若修改的c <= mid,则在树状数组上直接进行修改,同时把这个询问放在左边;否则直接放到右边,不修改;如果是查询操作,则看对应区间大于等于当前mid的数的个数(就是树状数组内记录的区间和)是否超过k,决定这个询问应该放在哪一边。
关于树状数组如何记录区间和,请访问我的这篇博客。
这道题要注意每次进入分治函数,树状数组都是要清空的,然而我们不能直接memset(会T),于是记录树状数组上每一位是否在本轮更新过(vis数组),如果没更新过,先清零再操作。
下面是我的代码。因为这道题求第k“大”很难受,于是我预处理,把数字重新排序,变成了第k小问题……于是代码长了些。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <complex>
#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 = 50005;
int n, m, times, vis[N], idx, seq[N], buf1[N], buf2[N];
ll ta[N], tb[N], lst[N], ans[N];
struct query{
int type, l, r;
ll k;
} q[N];
void single_add(int p, ll x){
for(int i = p; i <= n; i += i & -i){
if(vis[i] != times)
vis[i] = times, ta[i] = tb[i] = 0;
ta[i] += x, tb[i] += p * x;
}
}
void add(int l, int r, ll x){
single_add(l, x);
single_add(r + 1, -x);
}
ll single_ask(int p){
ll ret = 0;
for(int i = p; i; i -= i & -i)
if(vis[i] == times)
ret += (p + 1) * ta[i] - tb[i];
return ret;
}
ll ask(int l, int r){
return single_ask(r) - single_ask(l - 1);
}
void solve(int ql, int qr, int l, int r){
if(ql > qr) return;
if(l == r){
for(int i = ql; i <= qr; i++)
ans[seq[i]] = lst[l];
return;
}
times++;
int mid = (l + r) >> 1, p1 = 0, p2 = 0;
for(int i = ql; i <= qr; i++){
if(q[seq[i]].type == 1){
if(q[seq[i]].k <= mid){
add(q[seq[i]].l, q[seq[i]].r, 1), buf1[++p1] = seq[i];
}
else buf2[++p2] = seq[i];
}
else{
ll sum = ask(q[seq[i]].l, q[seq[i]].r);
if(sum < q[seq[i]].k) buf2[++p2] = seq[i], q[seq[i]].k -= sum;
else buf1[++p1] = seq[i];
}
}
for(int i = 1; i <= p1; i++) seq[ql + i - 1] = buf1[i];
for(int i = 1; i <= p2; i++) seq[ql + p1 - 1 + i] = buf2[i];
solve(ql, ql + p1 - 1, l, mid);
solve(ql + p1, qr, mid + 1, r);
}
int main(){
read(n), read(m);
for(int i = 1; i <= m; i++){
read(q[i].type), read(q[i].l), read(q[i].r), read(q[i].k);
if(q[i].type == 1) lst[++idx] = q[i].k;
}
sort(lst + 1, lst + idx + 1);
idx = unique(lst + 1, lst + idx + 1) - lst - 1;
for(int i = 1; i <= m; i++)
if(q[i].type == 1)
q[i].k = idx + 1 - (lower_bound(lst + 1, lst + idx + 1, q[i].k) - lst);
for(int i = 1, j = idx; i < j; i++, j--) swap(lst[i], lst[j]);
for(int i = 1; i <= m; i++) seq[i] = i;
solve(1, m, 1, idx);
for(int i = 1; i <= m; i++)
if(q[i].type == 2)
write(ans[i]), enter;
return 0;
}
BZOJ 3110 K大数查询 | 整体二分的更多相关文章
- 【BZOJ-3110】K大数查询 整体二分 + 线段树
3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 6265 Solved: 2060[Submit][Sta ...
- [ZJJOI2013]K大数查询 整体二分
[ZJJOI2013]K大数查询 链接 luogu 思路 整体二分. 代码 #include <bits/stdc++.h> #define ll long long using name ...
- BZOJ.3110.[ZJOI2013]K大数查询(整体二分 树状数组/线段树)
题目链接 BZOJ 洛谷 整体二分求的是第K小(利用树状数组).求第K大可以转为求第\(n-K+1\)小,但是这样好像得求一个\(n\). 注意到所有数的绝对值\(\leq N\),将所有数的大小关系 ...
- BZOJ 3110: [Zjoi2013]K大数查询 [整体二分]
有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. N ...
- BZOJ 3110 [Zjoi2013]K大数查询 ——整体二分
[题目分析] 整体二分显而易见. 自己YY了一下用树状数组区间修改,区间查询的操作. 又因为一个字母调了一下午. 貌似树状数组并不需要清空,可以用一个指针来维护,可以少一个log 懒得写了. [代码] ...
- P3332 [ZJOI2013]K大数查询 整体二分
终于入门整体二分了,勉勉强强算是搞懂了一个题目吧. 整体二分很多时候可以比较好的离线处理区间\(K\)大值的相关问题.考虑算法流程: 操作队列\(arr\),其中有询问和修改两类操作. 每次在答案的可 ...
- [BZOJ]3110 K大数查询(ZJOI2013)
这大概是唯一一道小C重写了4次的题目. 姿势不对的树套树(Fail) → 分块(Fail) → 整体二分(Succeed) → 树套树(Succeed). 让小C写点心得静静. Description ...
- BZOJ3110:[ZJOI2013]K大数查询(整体二分)
Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c.如果是2 a b c形式,表示询问从第a个位置到第b个位 ...
- [ZJOI2013]K大数查询——整体二分
题目描述 有N个位置,M个操作.操作有两种,每次操作如果是: 1 a b c:表示在第a个位置到第b个位置,每个位置加上一个数c 2 a b c:表示询问从第a个位置到第b个位置,第C大的数是多少. ...
随机推荐
- hover时显示可跟随鼠标移动的浮动框,运用函数节流与去抖进行优化
在很多笔试面试题中总能看到js函数去抖和函数节流,看过很多关于这两者的讨论,最近终于在一个需求中使用了函数去抖(debounce)和函数节流(throttle). 需要完成的效果是,鼠标在表格的单元格 ...
- centos 开机自启设定:
在sentos系统下,主要有两种方法设置自己安装的程序开机启动.1.把启动程序的命令添加到/etc/rc.d/rc.local文件中,比如下面的是设置开机启动httpd. #!/bin/sh # # ...
- centos7.6 安装配置rabbitmq
IP地址:192.168.200.108 安装erlang 和 依赖环境 yum install -y socat yum install -y erlang 安装rabbitmq yum insta ...
- 如何寻找无序数组中的第K大元素?
如何寻找无序数组中的第K大元素? 有这样一个算法题:有一个无序数组,要求找出数组中的第K大元素.比如给定的无序数组如下所示: 如果k=6,也就是要寻找第6大的元素,很显然,数组中第一大元素是24,第二 ...
- 利用Python编写Windows恶意代码!自娱自乐!勿用于非法用途!
本文主要展示的是通过使用python和PyInstaller来构建恶意软件的一些poc. 利用Python编写Windows恶意代码!自娱自乐!勿用于非法用途!众所周知的,恶意软件如果影响到了他人的生 ...
- 课堂实践ASL博客
实践博客 二分法查找元素 1.首先定义三个位置min,mid,max 2.每次从所有元素所处位置的中间开始查找(所有元素必须以由小及大顺序排列完毕) 3.当中间元素大于所查找元素时,从中间元素(mid ...
- 奔跑吧DKY——团队Scrum冲刺阶段-Day 7
今日完成任务 谭鑫:将人物图添加到游戏以及商店界面中,实现商店的选择换装功能 黄宇塘:制作人物图.背景图 赵晓海:阅读所有代码测试所有功能,美化部分界面 方艺雯:为商店界面及关于界面添加必要文字说明 ...
- 谈谈Keil 中C51的内存分配与优化
本帖最后由 Cresta 于 2014-1-21 10:49 编辑 看到这篇C51的内存分配和优化的文章,个人觉得分析的十分到位,在这里转给大家 C51的内存分配不同于一般的PC,内存空间有限,采 ...
- React---点击按钮实现内容复制功能
思路: 1.给要复制的内容容器添加一个标签(可以是ID,可以是类名等),通过dom技术获取该容器对象: 2.创建Range对象(某个区域内连续的内容),把该容器对象放进去: 3.将Range对象添加到 ...
- T4模板_T4基本结构
T4文本模板由 指令块.文本块.控制块 组成. 一. 指令块(MSDN文本模板指令) 指令块以@开头,基本的指令块包括<#@ template #> .<#@ parameter# ...