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. Revit开发小技巧-连接类

    该类用来连接两个Element.备注一下防止忘记.

  2. gym100676 [小熊骑士限定]2015 ACM Arabella Collegiate Programming Contest

    Kuma Rider久违的第二场训练,这场很水,又在vj的榜单上看到第一场的大哥了,2小时ak,大哥牛啤! A.水 #include<cstdio> #include<iostrea ...

  3. Netty源码分析第6章(解码器)---->第4节: 分隔符解码器

    Netty源码分析第六章: 解码器 第四节: 分隔符解码器 基于分隔符解码器DelimiterBasedFrameDecoder, 是按照指定分隔符进行解码的解码器, 通过分隔符, 可以将二进制流拆分 ...

  4. eclipse创建spring boot项目加载不到application.properties配置文件

    在配置文件application.properties中修改了端口号,但重启服务后发现端口号并没有跟着改变,发现是项目启动时没有加载application.properties文件导致 解决:项目-& ...

  5. Cocos2dx源码赏析(4)之Action动作

    Cocos2dx源码赏析(4)之Action动作 本篇,依然是通过阅读源码的方式来简单赏析下Cocos2dx中Action动画的执行过程.当然,这里也只是通过这种方式来总结下对Cocos2dx引擎的理 ...

  6. 请教JDBC中的thin和OCI的区别\

    请教JDBC中的thin和OCI的区别 https://zhidao.baidu.com/question/2267123737573204748.html

  7. Bing词典vs有道词典比对测试报告——功能篇之核心功能

    必应词典vs有道词典 核心功能对比 从应用的UI布局来看,这两款软件的功能如下: 相同 不同 必应词典 词典.例句.翻译 百科 有道词典 词典.例句.翻译 应用 就词典类软件来说,词典是最核心的功能. ...

  8. java实验三实验报告

    一.实验内容 1. XP基础 2. XP核心实践 3. 相关工具 二.实验过程(本次试验是在自己电脑上完成,没有使用实验楼) (一)敏捷开发与XP 1.XP是以开发符合客户需要的软件为目标而产生的一种 ...

  9. js实现把一个页面层数据传递到另一个页面

    由于之前面试,被问到过此问题,所以今天特意整理了一下.由于自己技术水平有限,若存在错误,欢迎提出批评. 本博客整理了两种方式从一个页面层向另一个页面层传递参数. 一. 通过cookie方式 1. 传递 ...

  10. Task 6.2冲刺会议五 /2015-5-18

    今天继续深入的看了看服务器的内容,修改了昨天的代码,发现网络编程还是很好玩的,感觉他的代码比平常写的更有趣一点,另外登陆界面也稍微看了一点.明天准备把登陆界面完善一下.