题目:

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. Django-REST-Framework JWT 实现SSO认证(下)

    在上一篇博客中,我已经对JSON Web 认证做了简单的解释,这篇博客是续篇,若不了解,请看上一篇博客:https://www.cnblogs.com/yushenglin/p/10863184.ht ...

  2. 框架之---Flask

    Flask是一个轻量级的WEB框架,它和Django相比,就是一个胖子和一个骨架的区别. Flask是一个py文件就可以run的web框架,但是就因为是py文件就能run,所有,啥都没有,等把Flas ...

  3. FJoi2017 1月20日模拟赛 恐狼后卫(口糊动规)

    Problem 1 恐狼后卫(wolf.cpp/c/pas) [题目描述] 著名卡牌游戏<石炉传说>中有一张随从牌:恐狼后卫.恐狼后卫的能力是使得相邻随从的攻击力提高. 现在有n张恐狼后卫 ...

  4. kendo grid 点击新增没有反映

    在datasource中缺少 editable: "inline",这一行

  5. 玲珑杯 ACM Round #10

    A 题意:给长度为n的序列染黑白色,要求连续的黑的格子数量<=a,连续的白的格子数量<=b,问方案总数,有多个询问 分析:递推 注意数据范围,是可以O(n)做的,所以可以直接递推 B 题意 ...

  6. 复习es6-let和const

    1.声明变量的方法 es5 : var   function es6 : var   function   let    const   class 2.let(const)与var 不同 let不能 ...

  7. 非常适合新手的jq/zepto源码分析05

    zepto的原型  $.fn  属性: constructor //构造行数 forEach: emptyArray.forEach, //都是原生数组的函数reduce: emptyArray.re ...

  8. jq页面提示或者页面牵引浏览--页面的指引向导插件

    1.看看插件效果吧 2. html 文件 :index.html <!DOCTYPE html> <html lang="en"> <head> ...

  9. Android: ADB not responding. You can wait more, or kill “adb.exe”

    Windows Only: Open a command prompt with administration permission and type netsh interface tcp set ...

  10. 拉普拉斯分布,高斯分布,L1 L2

    之前那篇文章里提到,L1其实是加上服从拉普拉斯分布的先验,L2是加上服从高斯分布的先验: http://www.cnblogs.com/charlesblc/p/7977732.html 那么记住拉普 ...