隐藏
「ZJOI2013」K大数查询 - 整体二分/CDQ分治+树状数组 | Bill Yang's Blog

路终会有尽头,但视野总能看到更远的地方。

0%

「ZJOI2013」K大数查询 - 整体二分/CDQ分治+树状数组

题目大意

有$n$个位置和$m$个操作。操作有两种,每次操作如果是$1\, a\,b \,c$的形式,表示往第$a$个位置到第$b$个位置每个位置加入一个数$c$。如果操作形如$2\,a\,b\,c$的形式,表示询问从第$a$个位置到第$b$个位置,第$c$大的数是多少。


整体二分

这道题可以使用树套树解决,但在这里我们提出一种更加神奇的做法,它的名字是:整体二分
整体二分是什么?其实这不是什么新东西,就是CDQ分治的变种。

整体二分的特殊在于它是对答案进行二分,而CDQ分治是对操作(时间轴)等进行二分。
为什么这种方法被称为整体二分?普通的二分是对某个单个元素进行的二分,而整体二分是对一个区间整体进行的二分,因此被称为整体二分。
整体二分的原理是二分答案,将操作对答案的影响根据二分的值划归在两个区间中依次继续二分。若每一层消耗的时间是$O(R-L)$,那么整体二分的复杂度则为$O(n\log n)$


题目分析

本题是一道整体二分的题,步骤如下:
我们用CDQBinary(s,t,L,R)表示处理$s\rightarrow t$的操作,答案在$L\rightarrow R$间。我们对$L$、$R$进行分治,而$s$、$t$是附属属性,如果是普通二分,$s==t$。

注意询问的是$k$大,需要转化为$k$小进行操作。

如果$L==R$,达到区间$[s,t]$答案。

处理区间$[s,t]$操作,如果是修改操作,插入的值$\le$二分出的$mid$,则在树状数组中更新区间个数,放入左区间,否则放入右区间。
如果是询问操作,使用树状数组得到区间的元素个数,若元素个数$\ge k$,将询问放入左区间,否则将$k$减去元素个数,放入右区间。

如果不会树状数组区间修改查询可以参考这里,讲的非常好。

清空树状数组,并递归左右区间。(若左右区间不存在不需要递归)


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
typedef long long LL;
inline const LL Get_Int() {
LL num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
const int maxn=50005;
struct Point {
int opt,id,l,r;
LL k;
} p[maxn];
int n;
LL ans[maxn];
struct BIT {
LL c1[maxn],c2[maxn];
#define lowbit(x) x&(-x)
void add(int x,LL v) {
for(int i=x; i<=n; i+=lowbit(i)) {
c1[i]+=v;
c2[i]+=x*v;
}
}
LL ask(int x) {
LL ans=0;
for(int i=x; i>0; i-=lowbit(i))ans+=(x+1)*c1[i]-c2[i];
return ans;
}
} bit;
void CDQBinary(int s,int t,LL Left,LL Right) {
if(s>t)return;
if(Left==Right) {
for(int i=s; i<=t; i++)
if(p[i].opt==2)ans[p[i].id]=Left;
return;
}
LL mid=(Left+Right)>>1;
vector<Point>q1,q2;
for(int i=s; i<=t; i++)
if(p[i].opt==1) {
if(p[i].k<=mid) {
bit.add(p[i].l,1);
bit.add(p[i].r+1,-1);
q1.push_back(p[i]);
} else q2.push_back(p[i]);
} else {
LL tmp=bit.ask(p[i].r)-bit.ask(p[i].l-1);
if(tmp>=p[i].k)q1.push_back(p[i]);
else p[i].k-=tmp,q2.push_back(p[i]);
}
for(int i=s; i<=t; i++)
if(p[i].opt==1&&p[i].k<=mid) {
bit.add(p[i].l,-1);
bit.add(p[i].r+1,1);
}
bool bj1=0,bj2=0;
for(int i=0; i<q1.size(); i++)p[i+s]=q1[i],bj1=1;
for(int i=0; i<q2.size(); i++)p[i+s+q1.size()]=q2[i],bj2=1;
if(bj1)CDQBinary(s,s+q1.size()-1,Left,mid);
if(bj2)CDQBinary(s+q1.size(),t,mid+1,Right);
}
int m,cnt=0;
int main() {
n=Get_Int();
m=Get_Int();
for(int i=1; i<=m; i++) {
p[i].opt=Get_Int();
p[i].l=Get_Int();
p[i].r=Get_Int();
p[i].k=Get_Int();
if(p[i].opt==1)p[i].k=n+1-p[i].k;
else p[i].id=++cnt;
}
CDQBinary(1,m,0,n<<1+2);
for(int i=1; i<=cnt; i++)printf("%d\n",n+1-ans[i]);
return 0;
}
姥爷们赏瓶冰阔落吧~