题目:

BZOJ3110

分析:

整体二分模板题……

先明确一下题意:每个位置可以存放多个数,第一种操作是“加入 (insert) ”一个数而不是“加上 (add) ”一个数。

首先考虑只有一次询问的情况。设询问的名次为\(k\),我们二分出一个答案\(mid\),然后遍历所有修改。建立一棵区间线段树(下标是位置的线段树),对于一个给\([a,b]\)区间加入一个数\(c\)的修改,如果\(c\geq mid\),就给\([a,b]\)这个区间整体加\(1\)。最后查询询问区间的和,即这个区间中不小于\(mid\)的数的数量。如果这个数量大于等于\(k\)则向上二分,并记录答案,否则向下二分。这样单次询问的复杂度是\(O(mlog_2^2n)\)。

可以看出,询问时最耗时间的是遍历所有修改并维护线段树。而这个操作只与当前二分到的\(mid\)有关,与询问无关。因此考虑把所有询问放在一个集合中,每次把询问分为将要“向上二分”(答案在\([mid+1,r]\))和“向下二分”(答案在\([l,mid]\)两个集合,并把可能对它们产生贡献的修改也分别加入这两个集合,然后分别递归下去。这就是“整体二分”。下面重点介绍如何对询问和修改分为两个集合。以下把修改和询问统称为“操作”。

询问的分法比较显然。如同只有一个询问的情况,把当前操作集合中的修改全部插入到线段树上。如果询问区间的和大于等于询问的名次则把这个询问分到“向上二分”的集合中,否则分到“向下二分”的集合中。

考虑修改。如果一个修改所加入的数不大于\(mid\),那么对于已经认定答案在\([mid+1,r]\)的询问一定是没有贡献的,所以只需要加到向下二分的集合中;如果一个修改所加入的数大于\(mid\),那么对于已经认定答案在\([l,mid]\)的询问一定是有\(1\)的贡献。如果把答案在\([l,mid]\)的询问所求的名次都减去\(1\),则这个修改也只会对向上二分的集合中的询问有贡献,只需要加到向上二分的集合中。这样每次都把所有操作分成独立的两部分,最多分\(log_2n\)次。每层所有操作集合的并集刚好是原集合,所以每层的时间复杂度是\(nlog_2n\),总复杂度\(O(nlog_2^2n)\)。具体流程可以参考代码。

代码:

并不需要真正把操作分成两个集合,只分它们的编号即可。

#include <cstdio>
#include <algorithm>
#include <cctype>
#include <cstring>
using namespace std; namespace zyt
{
template<typename T>
inline void read(T &x)
{
char c;
bool f = false;
x = 0;
do
c = getchar();
while (c != '-' && !isdigit(c));
if (c == '-')
f = true, c = getchar();
do
x = x * 10 + c - '0', c = getchar();
while (isdigit(c));
if (f)
x = -x;
}
template<typename T>
inline void write(T x)
{
static char buf[20];
char *pos = buf;
if (x < 0)
putchar('-'), x = -x;
do
*pos++ = x % 10 + '0';
while (x /= 10);
while (pos > buf)
putchar(*--pos);
}
typedef long long ll;
const int N = 5e4 + 10, B = 16, CHANGE = 1, QUERY = 2;
int n, m, id[N], ans[N];
struct node
{
int type, l, r;
ll c;
}opt[N];
namespace Segment_Tree
{
struct node
{
ll sum, tag;
}tree[1 << (B + 1)];
inline void update(const int rot)
{
tree[rot].sum = tree[rot << 1].sum + tree[rot << 1 | 1].sum;
}
inline void push_down(const int rot, const int lt, const int rt)
{
if (tree[rot].tag)
{
ll &tag = tree[rot].tag;
int mid = (lt + rt) >> 1;
tree[rot << 1].sum += tag * (mid - lt + 1);
tree[rot << 1].tag += tag;
tree[rot << 1 | 1].sum += tag * (rt - mid);
tree[rot << 1 | 1].tag += tag;
tag = 0;
}
}
void add(const int rot, const int lt, const int rt, const int ls, const int rs, const int x)
{
if (ls <= lt && rt <= rs)
{
tree[rot].sum += x * (rt - lt + 1), tree[rot].tag += x;
return;
}
int mid = (lt + rt) >> 1;
push_down(rot, lt, rt);
if (ls <= mid)
add(rot << 1, lt, mid, ls, rs, x);
if (rs > mid)
add(rot << 1 | 1, mid + 1, rt, ls, rs, x);
update(rot);
}
ll query(const int rot, const int lt, const int rt, const int ls, const int rs)
{
if (ls <= lt && rt <= rs)
return tree[rot].sum;
int mid = (lt + rt) >> 1;
ll ans = 0;
push_down(rot, lt, rt);
if (ls <= mid)
ans += query(rot << 1, lt, mid, ls, rs);
if (rs > mid)
ans += query(rot << 1 | 1, mid + 1, rt, ls, rs);
return ans;
}
}
void solve(const int idl, const int idr, const int l, const int r)
{//As for any question, determine
//wheather there are more than opt[i].c numbers greater than mid or not
//If so, ans[i] will be greater than mid. Otherwise less.
using Segment_Tree::query;
using Segment_Tree::add;
static int newl[N], newr[N];
if (l == r)
{
for (int i = idl; i <= idr; i++)
if (opt[id[i]].type == QUERY)
ans[id[i]] = l;
return;
}
int lcnt = 0, rcnt = 0;
int mid = (l + r) >> 1;
for (int i = idl; i <= idr; i++)
{
if (opt[id[i]].type == CHANGE)
{
if (opt[id[i]].c <= mid)
newl[lcnt++] = id[i];
else
{
add(1, 1, n, opt[id[i]].l, opt[id[i]].r, 1);
query(1, 1, n, 100, 100);
newr[rcnt++] = id[i];
}
}
else
{
ll tmp = query(1, 1, n, opt[id[i]].l, opt[id[i]].r);
if (tmp < opt[id[i]].c)
newl[lcnt++] = id[i], opt[id[i]].c -= tmp;
else
newr[rcnt++] = id[i];
}
}
for (int i = idl; i <= idr; i++)
if (opt[id[i]].type == CHANGE && opt[id[i]].c > mid)
add(1, 1, n, opt[id[i]].l, opt[id[i]].r, -1);
memcpy(id + idl, newl, sizeof(int[lcnt]));
memcpy(id + idl + lcnt, newr, sizeof(int[rcnt]));
if (lcnt)
solve(idl, idl + lcnt - 1, l, mid);
if (rcnt)
solve(idl + lcnt, idr, mid + 1, r);
}
int work()
{
read(n), read(m);
for (int i = 1; i <= m; i++)
{
read(opt[i].type);
read(opt[i].l);
read(opt[i].r);
read(opt[i].c);
id[i] = i;
}
solve(1, m, -N, N);
for (int i = 1; i <= m; i++)
if (opt[i].type == QUERY)
write(ans[i]), putchar('\n');
return 0;
}
}
int main()
{
freopen("3110.in", "r", stdin);
freopen("3110.out", "w", stdout);
return zyt::work();
}

【BZOJ3110】[ZJOI2013]K大数查询(整体二分)的更多相关文章

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

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

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

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

  3. 【bzoj3110】[Zjoi2013]K大数查询 整体二分+树状数组区间修改

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

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

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

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

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

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

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

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

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

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

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

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

    浅谈离线分治算法:https://www.cnblogs.com/AKMer/p/10415556.html 题目传送门:https://lydsy.com/JudgeOnline/problem.p ...

  10. BZOJ 3110 [ZJOI2013]K大数查询 (整体二分+线段树)

    和dynamic rankings这道题的思想一样 只不过是把树状数组换成线段树区间修改,求第$K$大的而不是第$K$小的 这道题还有负数,需要离散 #include <vector> # ...

随机推荐

  1. E - Cricket Field

    Description   Once upon a time there was a greedy King who ordered his chief Architect to build a fi ...

  2. Mongodb学习总结(2)——MongoDB与MySQL区别及其使用场景对比

    对于只有SQL背景的人来说,想要深入研究NoSQL似乎是一个艰巨的任务,MySQL与MongoDB都是开源常用数据库,但是MySQL是传统的关系型数据库,MongoDB则是非关系型数据库,也叫文档型数 ...

  3. CentOS服务器上部署 oracle10gr2

    1.下载Centos系统 Linux 镜像文件.         推荐使用 CentOS5.4,下载地址:http://isoredirect.centos.org/centos/5/isos/i38 ...

  4. C语言编程规范试题

    C语言编程规范试题 [说明]: 1.本试题中不考虑头文件引用问题(假定已经包含正确的头文件),C语言的标准函数都可用: 2.如果不特别说明,假定程序运行环境为:操作系统Windows 2000, VC ...

  5. A^B Mod C

    基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 给出3个正整数A B C,求A^B Mod C.   例如,3 5 8,3^5 Mod 8 = 3. Input 3个正整 ...

  6. [bzoj5301][Cqoi2018]异或序列_莫队

    异或序列 bzoj-5301 Cqoi-2018 题目大意:题目链接. 注释:略. 想法: 由于a^a=0这个性质,我们将所有的数变成异或前缀和. 所求就变成了求所有的$l_i\le x<y\l ...

  7. ZooKeeper实现配置中心的实例(原生API实现)(转)

    说明:要实现配置中心的例子,可以选择的SDK有很多,原生自带的SDK也是不错的选择.比如使用I0Itec,Spring Boot集成等. 大型应用通常会按业务拆分成一个个业务子系统,这些大大小小的子应 ...

  8. gn3 --iou

    http://www.wyzc.com/Course/Course/learnAction/id/14049/center/0#lesson/400847 http://www.mamicode.co ...

  9. HDFS学习笔记(2)hdfs_shell &amp; JavaAPI

    FileSystem shell指令 官方文档: HDFS Commands Reference appendToFile cat checksum chgrp chmod chown copyFro ...

  10. iOS 打开扬声器以及插入耳机的操作

    废话不多说说一下现状 网上好多关于扬声器的操作,可是问题多多.SDK7.X 和SDK7.X以上版本号有点诧异 #import <Foundation/Foundation.h> #impo ...