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大数查询 | 整体二分的更多相关文章

  1. 【BZOJ-3110】K大数查询 整体二分 + 线段树

    3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6265  Solved: 2060[Submit][Sta ...

  2. [ZJJOI2013]K大数查询 整体二分

    [ZJJOI2013]K大数查询 链接 luogu 思路 整体二分. 代码 #include <bits/stdc++.h> #define ll long long using name ...

  3. BZOJ.3110.[ZJOI2013]K大数查询(整体二分 树状数组/线段树)

    题目链接 BZOJ 洛谷 整体二分求的是第K小(利用树状数组).求第K大可以转为求第\(n-K+1\)小,但是这样好像得求一个\(n\). 注意到所有数的绝对值\(\leq N\),将所有数的大小关系 ...

  4. BZOJ 3110: [Zjoi2013]K大数查询 [整体二分]

    有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. N ...

  5. BZOJ 3110 [Zjoi2013]K大数查询 ——整体二分

    [题目分析] 整体二分显而易见. 自己YY了一下用树状数组区间修改,区间查询的操作. 又因为一个字母调了一下午. 貌似树状数组并不需要清空,可以用一个指针来维护,可以少一个log 懒得写了. [代码] ...

  6. P3332 [ZJOI2013]K大数查询 整体二分

    终于入门整体二分了,勉勉强强算是搞懂了一个题目吧. 整体二分很多时候可以比较好的离线处理区间\(K\)大值的相关问题.考虑算法流程: 操作队列\(arr\),其中有询问和修改两类操作. 每次在答案的可 ...

  7. [BZOJ]3110 K大数查询(ZJOI2013)

    这大概是唯一一道小C重写了4次的题目. 姿势不对的树套树(Fail) → 分块(Fail) → 整体二分(Succeed) → 树套树(Succeed). 让小C写点心得静静. Description ...

  8. BZOJ3110:[ZJOI2013]K大数查询(整体二分)

    Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c.如果是2 a b c形式,表示询问从第a个位置到第b个位 ...

  9. [ZJOI2013]K大数查询——整体二分

    题目描述 有N个位置,M个操作.操作有两种,每次操作如果是: 1 a b c:表示在第a个位置到第b个位置,每个位置加上一个数c 2 a b c:表示询问从第a个位置到第b个位置,第C大的数是多少. ...

随机推荐

  1. iOS分类Category探索

    什么是Category? Category是Objective-C 2.0之后添加的语言特性,Category的主要作用是为已经存在的类添加方法,一般称为分类,文件名格式是"NSObject ...

  2. linux下实现压测-html报表生成-控制台参数优化【jmeter】

    jmeter - 单机压测 - 命令行模式-html报表生成-控制台参数优化 一/ 准备工作 1.压力机安装并配置好 jdk 2.调试好程序脚本 再上传到 linux下 3.进入jmeter  bin ...

  3. GTK 预置对话框 GtkDialog 文件/颜色/字体选取等 GtkFileSelection

    (GTK2) 文档链接 作用:打开一个预置的对话框,如文件选取对话框 GtkFileSelection 效果下图所示 ╰── GtkDialog ├── GtkAboutDialog ├── GtkC ...

  4. CsvReader和CsvWriter操作csv文件

    使用方法: 提供把实例数据输出到磁盘csv文件的功能 提供读取csv文件,并封装成指定实例的功能 小工具自己依赖了slf4j+logbak,以及fastJson,如果与系统冲突,可以在pom文件中去除 ...

  5. linux递归查找文件内容并替换

    sed -i 's/原字符串/替换后字符串/g' `grep '搜索关键字' -rl /data/目标目录/ --include "*.html"` 上面是递归查找目录中所有的HT ...

  6. Mac 终端快捷键

    ctrl+A           跳转到行开头 ctrl+E           跳转到行结尾 ctrl+U           清空当前行 Command+K 清屏 Command+→多终端页面跳转 ...

  7. Spring笔记①--helloworld

    Spring Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架,它主要是为了解决企业应用开发的复杂性而诞生的: 目的:解决企业应用开发的复杂性 功能:使用基本的Javabean ...

  8. Leetcode题库——8.字符串转为整数【##】

    @author: ZZQ @software: PyCharm @file: myAtoi.py @time: 2018/9/20 20:54 要求:实现 atoi,将字符串转为整数. 1)根据需要丢 ...

  9. Leetcode题库——9.回文数

    @author: ZZQ @software: PyCharm @file: HuiWenShu.py @time: 2018/9/16 16:51 要求:判断一个整数是否是回文数.回文数是指正序(从 ...

  10. 基于CORBA/WEB技术构建三层体系结构的应用

    1 问题提出 在应用系统开发过程中,CLIENT/SERVER体系结构得到了广泛的应用 .其特点是,应用程序逻辑通常分布在客户和服务器两端,客户端发出数据资源访问请求,服务器端将结果返回客户端.但CL ...