终于入门整体二分了,勉勉强强算是搞懂了一个题目吧。

整体二分很多时候可以比较好的离线处理区间\(K\)大值的相关问题。考虑算法流程:

操作队列\(arr\),其中有询问和修改两类操作。

每次在答案的可行值域上二分一个\(mid\),把询问的答案\(>mid\)的分在\(R\)部,\(<=mid\)的分在\(L\)部。把修改的值\(>mid\)的分在\(R\)部,\(<=mid\)的分在\(L\)部。

何谓整体二分?就是直接一起二分所有的询问操作的答案,然后暴力扫描当前操作区间,将其划分为答案的左子区间与右子区间两个部分。

那么以什么为划分依据呢?看看这个操作对于左子区间有没有贡献。如果没有,那么就划分到右子区间中,然后将这个操作的权值更改为这个贡献减去所需的贡献,反之,则划分到左子区间中,同时将这个操作的贡献加入某一个容器,为询问操作服务。

这么说可能有点晕。就这道题说的话,应该是这样:

我们设尚未解决的操作区间为\([ql,qr]\),答案区间为[l,r][l,r],令当前答案为\(mid\)。

则若该操作是添加操作,如果其添加的\(C<=mid\),这此次操作对于左子区间有贡献,加入左子区间中,并将区间线段树中的区间\([q[i].l,q[i].r]\)整体加\(1\).

反之,则将操作加入到右子区间中。

若该操作是询问操作,如果当前的\(mid\)在线段树中查询到的,比它大的数的个数\(query()>=q[i].k\),则证明该询问操作应该在右子区间内可以找到答案。反之,则将\(q[i].k-=query()\),减去此次查询的贡献,然后将询问操作添加到左子区间中。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std; #define int long long
const int N = 50000 + 5; struct Ask {
int l, r, v, id, op; void read () {
cin >> op >> l >> r >> v;
} }q[N], tl[N], tr[N]; int ans[N], tag[N << 2], clr[N << 2], sum[N << 2]; int n, m, Q; #define lc (p << 1)
#define rc (p << 1 | 1)
#define mid ((l + r) >> 1) void pushdown (int p, int l, int r) {
if (clr[p]) {
clr[p] = 0;
tag[lc] = tag[rc] = 0;
sum[lc] = sum[rc] = 0;
clr[lc] = 1, clr[rc] = 1;
}
if (tag[p]) {
tag[lc] += tag[p];
tag[rc] += tag[p];
sum[lc] += tag[p] * (mid - l + 1);
sum[rc] += tag[p] * (r - mid);
tag[p] = 0;
}
} void push_up (int p) {
sum[p] = sum[lc] + sum[rc];
} void modify (int ql, int qr, int w, int p = 1, int l = 1, int r = n) {
if (ql <= l && r <= qr){
tag[p] += w;
sum[p] += w * (r - l + 1);
return;
}
pushdown (p, l, r);
if (ql <= mid) modify (ql, qr, w, lc, l, mid);
if (mid < qr) modify (ql, qr, w, rc, mid + 1, r);
push_up (p);
} int query (int ql, int qr, int p = 1, int l = 1, int r = n) {
if (ql <= l && r <= qr) {
return sum[p];
}
int ret = 0; pushdown(p,l,r);
if (ql <= mid) ret += query (ql, qr, lc, l, mid);
if (mid < qr) ret += query (ql, qr, rc, mid + 1, r);
return ret;
} void solve (int st, int en, int l, int r) {
// [st, en] -> 处理操作的左右端点
// [l, r] -> 对应值域
if (l == r) {
for (int i = st; i <= en; ++i) {
if (q[i].op == 2) ans[q[i].id] = l; // 查询
}
return;
}
int L = 0, R = 0;
clr[1] = 1; tag[1] = sum[1] = 0;
for (int i = st; i <= en; ++i) {
if (q[i].op == 1){
if (q[i].v > mid) { // > mid 的操作对于答案 <= mid 的询问不会影响
modify (q[i].l, q[i].r, 1);
tr[++R] = q[i];
} else {
tl[++L] = q[i];
}
} else {
int val = query (q[i].l, q[i].r);
if (val < q[i].v){
q[i].v -= val;
tl[++L] = q[i]; // L部答案 <= mid
}else{
tr[++R] = q[i]; // R部答案 > mid
}
}
}
for (int i = 1; i <= L; ++i) {
q[st + i - 1] = tl[i];
}
for (int i = L + 1; i <= L + R; ++i) {
q[st + i - 1] = tr[i - L];
}
solve (st, st + L - 1, l, mid);
solve (st + L, en, mid + 1, r);
} signed main () {
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
q[i].read ();
if (q[i].op == 2) {
q[i].id = ++Q;
}
}
solve (1, m, -n, n);
for (int i = 1; i <= Q; ++i) {
cout << ans[i] << endl;
}
}

P3332 [ZJOI2013]K大数查询 整体二分的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. 洛谷 P3332 [ZJOI2013]K大数查询 解题报告

    P3332 [ZJOI2013]K大数查询 题目描述 有\(N\)个位置,\(M\)个操作.操作有两种,每次操作如果是\(\tt{1\ a\ b\ c}\)的形式表示在第\(a\)个位置到第\(b\) ...

随机推荐

  1. Selenium 2自动化测试实战16(多窗口切换)

    一.多窗口切换 在页面操作过程中有时候点击某个链接会弹出新的窗口,这时就需要主机切换到新打开的窗口上进行操作.WebDriver提供了switch_to.window()方法.可以实现在不同的窗口之间 ...

  2. 十一:jinja2模板传参

    从后台传参到模板,模板再渲染到前端 传参的时候,可以在html后面加上关键字传参,在模板里面用{{ 参数 }}使用即可,可以传多个参数 也可以使用**传参,取值的时候就直接取内容

  3. CentOS 7 Docker 安装

    CentOS Docker 安装 Docker支持以下的CentOS版本: CentOS 7 (64-bit) CentOS 6.5 (64-bit) 或更高的版本 本文以 CentOS 7.6 版本 ...

  4. goland搭建beego开发环境

    1.安装最新的go软件 ,当前版本1.122.下载goland开发工具3.安装bee工具 go get github.com/beego/bee4.通过bee api dsh -tables=&quo ...

  5. 基于高斯分布的异常检测(Anomaly Detection)算法

    记得在做电商运营初期,每每为我们频道的促销活动锁取得的“超高”销售额感动,但后来随着工作的深入,我越来越觉得这里面水很深.商家运营.品类运营不断的通过刷单来获取其所需,或是商品搜索排名,或是某种kpi ...

  6. ios模拟器快捷键

    shift+cmd+h  返回桌面 cmd+5或者4或者3  可以直接调节大小 cmd+R运行项目 cmd+R弹出键盘 ios模拟器弹出键盘 在xcode6中, 模拟器中的键盘和电脑的键盘可以进行绑定 ...

  7. Jackson快速入门

    1.Jackson的简单用法 2.Jackson框架的高阶应用 3.Jackson不支持Java8 Date解决方法 https://www.cnblogs.com/mkxzy/p/7091381.h ...

  8. Mybatis(二) SQL映射文件

    SQL映射文件 单条件查询 1. 在UserMapper接口添加抽象方法 //根据用户名模糊查询 List<User> getUserListByName(); 2. 在UserMappe ...

  9. [ZJOI2008]骑士 题解

    题面 这道题稍微想一想就会联想到树形DP的入门题:没有上司的舞会: 但是再想一想会发现这根本就不是一颗树,因为它比树多了一条边: 这时候我们引入一个新的概念:基环树: 顾名思义(??),基环树就是在一 ...

  10. orm的设计思路

    一,我们先搞懂什么是orm? ORM:对象关系映射(Object Relational Mapping,简称ORM),目的是想像操作对象一样操作数据库.因为数据库不是面向对象的,所以需要编程进行映射. ...