参考:https://www.luogu.org/blog/Owencodeisking/post-xue-xi-bi-ji-cdq-fen-zhi-hu-zheng-ti-er-fen

前置技能:树状数组,线段树,分治、归并排序

CDQ分治:

据说是OI大佬陈丹琦发明的

1.三维偏序

思路:

第一维排序,第二维分治,第三维树状数组上查询

考虑分治时区间 [l, m] 对区间 [m+1, r] 的贡献,因为第一维已经排好序,所以区间 [l, m] 的第一维小于区间 [m+1, r]的第一维

然后对于区间 [m+1, r]中的某个元素x,将区间 [l, m] 的第二维小于x的元素的按第三维的权值加入树状数组,

最后区间 [l, m] 对区间 x 的贡献就是查询树状数组中小于x第三维的个数

可以边进行分治边进行归并排序,树状数组要及时清空

通过画图我们可以发现,对于每个位置,我们在分治时,它之前的位置对它的贡献都计算过了,所以这种方法是正确的。

因为递归的层数是log(n)层,再加上树状数组,所以时间复杂度是O(n*log(n)^2)

P3810 【模板】三维偏序(陌上花开) 

代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
//#define mp make_pair
#define pb push_back
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdi pair<double, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n";
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head const int N = 1e5 + , M = 2e5 + ;
struct Node {
int x, y, z;
int ans, cnt;
bool operator < (const Node & rhs) const {
if(x == rhs.x) {
if(y == rhs.y) return z < rhs.z;
else return y < rhs.y;
}
else return x < rhs.x;
}
}a[N], tmp[N];
int bit[M], res[N], n, k, cnt = ;
void add(int x, int a) {
while(x <= k) bit[x] += a, x += x&-x;
}
int sum(int x) {
int res = ;
while(x) res += bit[x], x -= x&-x;
return res;
}
void cdq(int l, int r) {
if(l == r) {
a[l].ans += a[l].cnt-;
return ;
}
int m = l+r >> ;
cdq(l, m);
cdq(m+, r);
int p = l, q = m+, tp = l;
while(q <= r) {
while(p <= m && a[p].y <= a[q].y) add(a[p].z, a[p].cnt), tmp[tp++] = a[p], ++p;
a[q].ans += sum(a[q].z);
tmp[tp++] = a[q];
++q;
}
for (int i = l; i < p; ++i) add(a[i].z, -a[i].cnt);
while(p <= m) tmp[tp++] = a[p], ++p;
for (int i = l; i <= r; ++i) a[i] = tmp[i];
}
int main() {
scanf("%d %d", &n, &k);
for (int i = ; i <= n; ++i) scanf("%d %d %d", &a[i].x, &a[i].y, &a[i].z);
sort(a+, a++n);
int now = ;
for (int i = ; i <= n; ++i) {
if(a[i].x == a[i-].x && a[i].y == a[i-].y && a[i].z == a[i-].z) ++now;
else {
a[++cnt] = a[i-];
a[cnt].cnt = now;
a[cnt].ans = ;
now = ;
}
}
a[++cnt] = a[n];
a[cnt].cnt = now;
a[cnt].ans = ;
cdq(, cnt);
for (int i = ; i <= cnt; ++i) res[a[i].ans] += a[i].cnt;
for (int i = ; i < n; ++i) printf("%d\n", res[i]);
return ;
}

例题1:CodeForces - 669E

思路:时间看成一个维度就转换成了三维偏序了

代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
//#define mp make_pair
#define pb push_back
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdi pair<double, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n";
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head const int N = 1e5 + ;
struct node {
int a, t, x, ans, id;
bool operator < (const node & rhs) const {
return id < rhs.id;
}
}a[N], tmp[N];
int n;
map<int, int> cnt;
void cdq(int l, int r) {
if(l == r) return ;
int m = l+r >> ;
cdq(l, m);
cdq(m+, r);
int p = l, q = m+, tp = l;
while(q <= r) {
while(p <= m && a[p].t <= a[q].t) {
if(a[p].a == ) cnt[a[p].x]++;
else if(a[p].a == ) cnt[a[p].x]--;
tmp[tp++] = a[p], ++p;
}
if(a[q].a == ) a[q].ans += cnt[a[q].x];
tmp[tp++] = a[q];
++q;
}
for (int i = l; i < p; ++i) {
if(a[i].a == ) cnt[a[i].x]--;
else if(a[i].a == ) cnt[a[i].x]++;
}
while(p <= m) tmp[tp++] = a[p], ++p;
for (int i = l; i <= r; ++i) a[i] = tmp[i];
}
int main() {
scanf("%d", &n);
for (int i = ; i <= n; ++i) scanf("%d %d %d", &a[i].a, &a[i].t, &a[i].x), a[i].ans = , a[i].id = i;
cdq(, n);
sort(a+, a++n);
for (int i = ; i <= n; ++i) if(a[i].a == ) printf("%d\n", a[i].ans);
return ;
}

例题2:HDU - 5618

思路:由于对于每个点都要询问,所以不能像陌上花开那样缩点了,排序后把相同的点的贡献先加上去

代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
//#define mp make_pair
#define pb push_back
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdi pair<double, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n";
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head const int N = 1e5 + ;
struct Node {
int x, y, z, id;
int cnt;
bool operator < (const Node & rhs) const {
if(x == rhs.x) {
if(y == rhs.y) return z < rhs.z;
else return y < rhs.y;
}
else return x < rhs.x;
}
}a[N], tmp[N];
int bit[N], ans[N], n, cnt = ;
void add(int x, int a) {
while(x < N) bit[x] += a, x += x&-x;
}
int sum(int x) {
int res = ;
while(x) res += bit[x], x -= x&-x;
return res;
}
void cdq(int l, int r) {
if(l == r) return ;
int m = l+r >> ;
cdq(l, m);
cdq(m+, r);
int p = l, q = m+, tp = l;
while(q <= r) {
while(p <= m && a[p].y <= a[q].y) add(a[p].z, a[p].cnt), tmp[tp++] = a[p], ++p;
ans[a[q].id] += sum(a[q].z);
tmp[tp++] = a[q];
++q;
}
for (int i = l; i < p; ++i) add(a[i].z, -a[i].cnt);
while(p <= m) tmp[tp++] = a[p], ++p;
for (int i = l; i <= r; ++i) a[i] = tmp[i];
}
int T;
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for (int i = ; i <= n; ++i) scanf("%d %d %d", &a[i].x, &a[i].y, &a[i].z), a[i].id = i, a[i].cnt = , ans[i] = ;
sort(a+, a++n);
int now = ;
for (int i = n-; i >= ; --i) {
if(a[i].x == a[i+].x && a[i].y == a[i+].y && a[i].z == a[i+].z) ++now;
else now = ;
ans[a[i].id] += now;
}
cdq(, n);
for (int i = ; i <= n; ++i) printf("%d\n", ans[i]);
}
return ;
}

例题3:CodeChef - QRECT

思路:考虑用容斥,用总个数减去和横纵坐标和它不相交的个数,这样我们发现和它横纵坐标都不相交被减了两次,也就是四个角上的矩形,四个角上的矩形的个数就是三维偏序问题

代码:

#include<bits/stdc++.h>
using namespace std;
#define y1 y11
#define pb push_back
//head const int N = 1e5 + , M = 2e5 + ;
struct Node {
int ty, x1, y1, x2, y2, id;
int ans, cnt;
bool operator < (const Node & rhs) const {
return id < rhs.id;
}
}a[N], aa[N], tmp[N];
vector<int> vx, vy;
int n, bit1[M], bit2[M], bit[M], p, pos[N], cnt = , now = ;
char op[];
void add(int x, int a, int *bit) {
while(x < M) bit[x] += a, x += x&-x;
}
int sum(int x, int *bit) {
int res = ;
while(x) res += bit[x], x -= x&-x;
return res;
}
void init() {
for (int i = ; i < M; ++i) bit1[i] = bit2[i] = ;
}
void cdq1(int l, int r) {
if(l == r) return ;
int m = l+r >> ;
cdq1(l, m);
cdq1(m+, r);
int p = l, q = m+, tp = l;
while(q <= r) {
while(p <= m && aa[p].x1 < aa[q].x1) {
if(aa[p].ty == ) add(aa[p].y1, , bit);
else if(aa[p].ty == ) add(aa[p].y1, -, bit);
tmp[tp++] = aa[p];
++p;
}
if(aa[q].ty == ) {
aa[q].ans += sum(aa[q].y1-, bit);
}
tmp[tp++] = aa[q];
++q;
}
for (int i = l; i < p; ++i) if(aa[i].ty == ) add(aa[i].y1, -, bit); else if(aa[i].ty == ) add(aa[i].y1, , bit);
while(p <= m) tmp[tp++] = aa[p], ++p;
for (int i = l; i <= r; ++i) aa[i] = tmp[i];
} void cdq2(int l, int r) {
if(l == r) return ;
int m = l+r >> ;
cdq2(l, m);
cdq2(m+, r);
int p = l, q = m+, tp = l;
while(q <= r) {
while(p <= m && aa[p].x1 > aa[q].x1) {
if(aa[p].ty == ) add(aa[p].y1, , bit);
else if(aa[p].ty == ) add(aa[p].y1, -, bit);
tmp[tp++] = aa[p];
++p;
}
if(aa[q].ty == ) {
aa[q].ans += sum(M-, bit) - sum(aa[q].y1, bit);
}
tmp[tp++] = aa[q];
++q;
}
for (int i = l; i < p; ++i) if(aa[i].ty == ) add(aa[i].y1, -, bit); else if(aa[i].ty == ) add(aa[i].y1, , bit);
while(p <= m) tmp[tp++] = aa[p], ++p;
for (int i = l; i <= r; ++i) aa[i] = tmp[i];
} void cdq3(int l, int r) {
if(l == r) return ;
int m = l+r >> ;
cdq3(l, m);
cdq3(m+, r);
int p = l, q = m+, tp = l;
while(q <= r) {
while(p <= m && aa[p].x1 < aa[q].x1) {
if(aa[p].ty == ) add(aa[p].y1, , bit);
else if(aa[p].ty == ) add(aa[p].y1, -, bit);
tmp[tp++] = aa[p];
++p;
}
if(aa[q].ty == ) {
aa[q].ans += sum(M-, bit) - sum(aa[q].y1, bit);
}
tmp[tp++] = aa[q];
++q;
}
for (int i = l; i < p; ++i) if(aa[i].ty == ) add(aa[i].y1, -, bit); else if(aa[i].ty == ) add(aa[i].y1, , bit);
while(p <= m) tmp[tp++] = aa[p], ++p;
for (int i = l; i <= r; ++i) aa[i] = tmp[i];
} void cdq4(int l, int r) {
if(l == r) return ;
int m = l+r >> ;
cdq4(l, m);
cdq4(m+, r);
int p = l, q = m+, tp = l;
while(q <= r) {
while(p <= m && aa[p].x1 > aa[q].x1) {
if(aa[p].ty == ) add(aa[p].y1, , bit);
else if(aa[p].ty == ) add(aa[p].y1, -, bit);
tmp[tp++] = aa[p];
++p;
}
if(aa[q].ty == ) {
aa[q].ans += sum(aa[q].y1-, bit);
}
tmp[tp++] = aa[q];
++q;
}
for (int i = l; i < p; ++i) if(aa[i].ty == ) add(aa[i].y1, -, bit); else if(aa[i].ty == ) add(aa[i].y1, , bit);
while(p <= m) tmp[tp++] = aa[p], ++p;
for (int i = l; i <= r; ++i) aa[i] = tmp[i];
}
int main() {
scanf("%d", &n);
for (int i = ; i <= n; ++i) {
scanf("%s", op);
if(op[] == 'I') {
scanf("%d %d %d %d", &a[i].x1, &a[i].y1, &a[i].x2, &a[i].y2);
if(a[i].x1 > a[i].x2) swap(a[i].x1, a[i].x2);
if(a[i].y1 > a[i].y2) swap(a[i].y1, a[i].y2);
vx.pb(a[i].x1);vx.pb(a[i].x2);vy.pb(a[i].y1);vy.pb(a[i].y2);
a[i].ty = ;
a[i].id = i;
pos[++now] = i;
++cnt;
}
else if(op[] == 'D') {
scanf("%d", &p);
a[i] = a[pos[p]];
a[i].ty = ;
a[i].id = i;
--cnt;
}
else {
scanf("%d %d %d %d", &a[i].x1, &a[i].y1, &a[i].x2, &a[i].y2);
if(a[i].x1 > a[i].x2) swap(a[i].x1, a[i].x2);
if(a[i].y1 > a[i].y2) swap(a[i].y1, a[i].y2);
vx.pb(a[i].x1);vx.pb(a[i].x2);vy.pb(a[i].y1);vy.pb(a[i].y2);
a[i].ty = ;
a[i].id = i;
a[i].cnt = a[i].ans = cnt;
}
}
sort(vx.begin(), vx.end());
vx.erase(unique(vx.begin(), vx.end()), vx.end());
sort(vy.begin(), vy.end());
vy.erase(unique(vy.begin(), vy.end()), vy.end());
for (int i = ; i <= n; ++i) {
a[i].x1 = lower_bound(vx.begin(), vx.end(), a[i].x1) - vx.begin() + ;
a[i].x2 = lower_bound(vx.begin(), vx.end(), a[i].x2) - vx.begin() + ;
a[i].y1 = lower_bound(vy.begin(), vy.end(), a[i].y1) - vy.begin() + ;
a[i].y2 = lower_bound(vy.begin(), vy.end(), a[i].y2) - vy.begin() + ;
}
for (int i = ; i <= n; ++i) {
if(a[i].ty == ) add(a[i].x1, , bit1), add(a[i].x2, , bit2);
else if(a[i].ty == ) add(a[i].x1, -, bit1), add(a[i].x2, -, bit2);
else a[i].ans -= sum(a[i].x1-, bit2) + (sum(M-, bit1) - sum(a[i].x2, bit1));
}
init();
for (int i = ; i <= n; ++i) {
if(a[i].ty == ) add(a[i].y1, , bit1), add(a[i].y2, , bit2);
else if(a[i].ty == ) add(a[i].y1, -, bit1), add(a[i].y2, -, bit2);
else a[i].ans -= sum(a[i].y1-, bit2) + (sum(M-, bit1) - sum(a[i].y2, bit1));
} for (int i = ; i <= n; ++i) {
aa[i] = a[i];
if(a[i].ty <= ) aa[i].x1 = a[i].x2, aa[i].y1 = a[i].y2;
}
cdq1(, n);
sort(aa+, aa++n);
for (int i = ; i <= n; ++i) a[i].ans = aa[i].ans; for (int i = ; i <= n; ++i) {
aa[i] = a[i];
if(a[i].ty == ) aa[i].x1 = a[i].x2, aa[i].y1 = a[i].y2;
}
cdq2(, n);
sort(aa+, aa++n);
for (int i = ; i <= n; ++i) a[i].ans = aa[i].ans; for (int i = ; i <= n; ++i) {
aa[i] = a[i];
if(a[i].ty <= ) aa[i].x1 = a[i].x2, aa[i].y1 = a[i].y1;
else aa[i].x1 = a[i].x1, aa[i].y1 = a[i].y2;
}
cdq3(, n);
sort(aa+, aa++n);
for (int i = ; i <= n; ++i) a[i].ans = aa[i].ans; for (int i = ; i <= n; ++i) {
aa[i] = a[i];
if(a[i].ty <= ) aa[i].x1 = a[i].x1, aa[i].y1 = a[i].y2;
else aa[i].x1 = a[i].x2, aa[i].y1 = a[i].y1;
}
cdq4(, n);
sort(aa+, aa++n);
for (int i = ; i <= n; ++i) a[i].ans = aa[i].ans; for (int i = ; i <= n; ++i) if(a[i].ty == ) printf("%d\n", a[i].ans);
return ;
}

例题4:P3157 [CQOI2011]动态逆序对

思路:将删除的时间看成一个维度

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 1e5 + ;
struct Node {
int t, v, pos, id;
bool operator < (const Node & rhs) const {
return t < rhs.t;
}
}a[N], tmp[N];
int pos[N], ans[N], n, m, b;
LL res = ;
struct BIT {
int bit[N];
void init() {
for (int i = ; i <= n; ++i) bit[i] = ;
}
void add(int x, int a) {
while(x <= n) bit[x] += a, x += x&-x;
}
int sum(int x) {
int res = ;
while(x) res += bit[x], x -= x&-x;
return res;
}
}B;
void cdq1(int l, int r) {
if(l == r) return ;
int m = l+r >> ;
cdq1(l, m); cdq1(m+, r);
int p = l, q = m+, tp = l;
while(q <= r) {
while(p <= m && a[p].pos < a[q].pos) {
B.add(a[p].v, );
tmp[tp++] = a[p];
++p;
}
ans[a[q].id] += B.sum(n) - B.sum(a[q].v);
tmp[tp++] = a[q];
++q;
}
for (int i = l; i < p; ++i) B.add(a[i].v, -);
while(p <= m) tmp[tp++] = a[p], ++p;
for (int i = l; i <= r; ++i) a[i] = tmp[i];
}
void cdq2(int l, int r) {
if(l == r) return ;
int m = l+r >> ;
cdq2(l, m); cdq2(m+, r);
int p = l, q = m+, tp = l;
while(q <= r) {
while(p <= m && a[p].pos > a[q].pos) {
B.add(a[p].v, );
tmp[tp++] = a[p];
++p;
}
ans[a[q].id] += B.sum(a[q].v-);
tmp[tp++] = a[q];
++q;
}
for (int i = l; i < p; ++i) B.add(a[i].v, -);
while(p <= m) tmp[tp++] = a[p], ++p;
for (int i = l; i <= r; ++i) a[i] = tmp[i];
}
int main() {
scanf("%d %d", &n, &m);
for (int i = ; i <= n; ++i) {
scanf("%d", &a[i].v);
a[i].pos = i;
a[i].id = ;
a[i].t = ;
pos[a[i].v] = i;
}
for (int i = ; i <= n; ++i) {
res += B.sum(n) - B.sum(a[i].v);
B.add(a[i].v, );
}
B.init();
for (int i = ; i <= m; ++i) {
scanf("%d", &b);
a[pos[b]].id = i;
a[pos[b]].t = m-i+;
}
sort(a+, a++n);
cdq1(, n);
sort(a+, a++n);
cdq2(, n);
for (int i = ; i <= m; ++i) {
printf("%lld\n", res);
res -= ans[i];
}
return ;
}

例题5:UVALive - 6667

思路:严格三维偏序,在对x排序时,如果x相同,按y从大到小排序,这样x相同是左边区间就不会对右边区间产生贡献了,因为这时左边y大于等于右边y

代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
//#define mp make_pair
#define pb push_back
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdi pair<double, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n";
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head const int N = 3e5 + , MM = 1e6 + ;
struct Node {
int x, y, z, len;
}a[N];
struct BIT {
int bit[MM];
void clr(int x) {
while(x < MM) bit[x] = , x += x&-x;
}
void add(int x, int a) {
while(x < MM) bit[x] = max(bit[x], a), x += x&-x;
}
int mx(int x) {
int res = ;
while(x) res = max(res, bit[x]), x -= x&-x;
return res;
}
}b;
bool cmp1(Node a, Node b) {
if(a.x == b.x) return a.y > b.y;
else return a.x < b.x;
}
bool cmp2(Node a, Node b) {
return a.y < b.y;
}
void cdq(int l, int r) {
if(l == r) return ;
int m = l+r >> ;
cdq(l, m);
sort(a+l, a+m+, cmp2);
sort(a+m+, a+r+, cmp2);
int p = l, q = m+;
while(q <= r) {
while(p <= m && a[p].y < a[q].y) b.add(a[p].z, a[p].len), ++p;
a[q].len = max(a[q].len, b.mx(a[q].z-)+);
++q;
}
for (int i = l; i < p; ++i) b.clr(a[i].z);
sort(a+m+, a+r+, cmp1);
cdq(m+, r);
}
int A, B, C = ~(<<), M = (<<)-, m, n;
int r() {
A = * (A & M) + (A >> );
B = * (B & M) + (B >> );
return (C & ((A << ) + B)) % ;
}
int main() {
while(~scanf("%d %d %d %d", &m, &n, &A, &B)) {
if(!m && !n && !A && !B) break;
for (int i = ; i <= m; ++i) {
scanf("%d %d %d", &a[i].x, &a[i].y, &a[i].z);
a[i].z++;
a[i].len = ;
}
for (int i = ; i <= n; ++i) {
++m;
a[m].x = r();
a[m].y = r();
a[m].z = r();
a[m].z++;
a[m].len = ;
} sort(a+, a++m, cmp1);
cdq(, m);
int ans = ;
for (int i = ; i <= m; ++i) ans = max(ans, a[i].len);
printf("%d\n", ans);
}
return ;
}

2.四维偏序

参考:http://www.cnblogs.com/candy99/p/6442434.html

cdq套cdq

假设四维为(a, b, c, d),在进行普通的cdq分治时,我们归并排序使得b有序,这个时候就可以再套一个cdq来解决(b, c, d)的三维偏序问题,但是这时不要忘记了a的作用,

我们要求a有序时左边[l, m]对右边[m+1, r]的贡献,所以在套之前给左边[l, m]的元素打个标记来区分,然后就可以愉快地套cdq了。时间复杂度显然为O(n*log(n)^3)。

然后原题找不到了,做一道稍微复杂点的,不要忘记第二个cdq是在新数组上进行,如果在原数组上进行的话,做完以后b就不是有序的了。

HDU - 5126

思路:将一个查询用容斥拆分成8个查询

代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
//#define mp make_pair
#define pb push_back
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdi pair<double, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n";
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head const int N = 5e4 + ;
struct Node {
int ty, x, y, z, c, id;
bool flag;
}a[N*], t1[N*], t2[N*];
vector<int> vc;
int T, n, ty, x1, y1, z1, x2, y2, z2, tot = , ans[N], cnt = , sz;
struct BIT {
int bit[N*];
void add(int x, int a) {
while(x <= sz) bit[x] += a, x += x&-x;
}
int sum(int x) {
int res = ;
while(x) res += bit[x], x -= x&-x;
return res;
}
}B;
void cdq1(int l, int r){
if(l == r) return ;
int m = l+r >> ;
cdq1(l, m); cdq1(m+, r);
int p = l, q = m+, tp = l;
Node *a = t1, *t = t2;
while(q <= r) {
while(p <= m && a[p].y <= a[q].y) {
if(a[p].flag && a[p].ty == ) B.add(a[p].z, );
t[tp++] = a[p];
++p;
}
if(!a[q].flag && a[q].ty == ) ans[a[q].id] += a[q].c*B.sum(a[q].z);
t[tp++] = a[q];
++q;
}
for (int i = l; i < p; ++i) if(a[i].flag && a[i].ty == ) B.add(a[i].z, -);
while(p <= m) t[tp++] = a[p], ++p;
for (int i = l; i <= r; ++i) a[i] = t[i];
}
void cdq(int l, int r) {
if(l == r) return ;
int m = l+r >> ;
cdq(l, m); cdq(m+, r);
int p = l, q = m+, tp = l;
Node *t = t1;
while(q <= r) {
while(p <= m && a[p].x <= a[q].x) t[tp] = a[p], t[tp].flag = , ++tp, ++p;
t[tp] = a[q];
t[tp].flag = ;
++tp;
++q;
}
while(p <= m) t[tp] = a[p], t[tp].flag = , ++tp, ++p;
for (int i = l; i <= r; ++i) a[i] = t[i];
cdq1(l, r);
}
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
tot = cnt = ;
for (int i = ; i <= n; ++i) {
scanf("%d", &ty);
if(ty == ) {
++tot;
a[tot].ty = ;
scanf("%d %d %d", &a[tot].x, &a[tot].y, &a[tot].z);
}
else {
scanf("%d %d %d %d %d %d", &x1, &y1, &z1, &x2, &y2, &z2);
++cnt;
ans[cnt] = ;
a[++tot] = {, x2, y2, z2, , cnt, };
a[++tot] = {, x1-, y2, z2, -, cnt, };
a[++tot] = {, x2, y1-, z2, -, cnt, };
a[++tot] = {, x2, y2, z1-, -, cnt, };
a[++tot] = {, x1-, y1-, z2, , cnt, };
a[++tot] = {, x1-, y2, z1-, , cnt, };
a[++tot] = {, x2, y1-, z1-, , cnt, };
a[++tot] = {, x1-, y1-, z1-, -, cnt, };
}
}
vc.clear();
for (int i = ; i <= tot; ++i) vc.pb(a[i].z);
sort(vc.begin(), vc.end());
vc.erase(unique(vc.begin(), vc.end()), vc.end());
for (int i = ; i <= tot; ++i) a[i].z = lower_bound(vc.begin(), vc.end(), a[i].z) - vc.begin()+;
sz = (int)vc.size();
cdq(, tot);
for (int i = ; i <= cnt; ++i) printf("%d\n", ans[i]);
}
return ;
}

3.其他

cdq优化dp等。

例题1:HYSBZ - 4553

思路:设mx[i]为第i个的最大值,mn[i]为最小值

dp[i] = max{dp[j] + 1} 其中 j < i, mx[j] <= a[i], a[j] <= mn[i]

这是个三维偏序问题,可以用cdq维护dp的转移,具体看代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
//#define mp make_pair
#define pb push_back
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdi pair<double, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n";
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head const int N = 1e5 + ;
struct Node {
int v, mx, mn, id, dp;
}a[N];
struct BIT {
int bit[N];
void clr(int x) {
while(x < N) bit[x] = , x += x&-x;
}
void add(int x, int a) {
while(x < N) bit[x] = max(bit[x], a), x += x&-x;
}
int mx(int x) {
int res = ;
while(x) res = max(res, bit[x]), x -= x&-x;
return res;
}
}b;
bool cmp1(Node a, Node b) {
return a.mx < b.mx;
}
bool cmp2(Node a, Node b) {
return a.v < b.v;
}
bool cmp3(Node a, Node b) {
return a.id < b.id;
}
void cdq(int l, int r) {
if(l == r) return ;
int m = l+r >> ;
cdq(l, m);
sort(a+l, a+m+, cmp1);
sort(a+m+, a+r+, cmp2);
int p = l, q = m+;
while(q <= r) {
while(p <= m && a[p].mx <= a[q].v) b.add(a[p].v, a[p].dp), ++p;
a[q].dp = max(a[q].dp, b.mx(a[q].mn)+);
++q;
}
for (int i = l; i < p; ++i) b.clr(a[i].v);
sort(a+m+, a+r+, cmp3);
cdq(m+, r);
}
int n, m, x, y;
int main() {
scanf("%d %d", &n, &m);
for (int i = ; i <= n; ++i) {
scanf("%d", &a[i].v);
a[i].mn = a[i].mx = a[i].v;
a[i].id = i;
a[i].dp = ;
}
for (int i = ; i <= m; ++i) {
scanf("%d %d", &x, &y);
a[x].mx = max(a[x].mx, y);
a[x].mn = min(a[x].mn, y);
}
cdq(, n);
int ans = ;
for (int i = ; i <= n; ++i) ans = max(ans, a[i].dp);
printf("%d\n", ans);
return ;
}

例题2:HDU - 4742

思路:同样是dp,用bit维护最大值和最大值的个树,有点小技巧

代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
//#define mp make_pair
#define pb push_back
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdi pair<double, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n";
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head const int N = 1e5 + ;
struct Node {
int x, y, z, len, cnt;
}a[N];
vector<int> vc;
int T, n, sz;
struct BIT {
pii bit[N];
void modify(pii &a, pii b) {
if(b.fi == a.fi) a.se += b.se;
else if(b.fi > a.fi) a = b;
}
void clr(int x) {
while(x <= sz) bit[x] = {, }, x += x&-x;
}
void add(int x, pii a) {
while( x <= sz) modify(bit[x], a), x += x&-x;
}
pii mx(int x) {
pii res = {, };
while(x) modify(res, bit[x]), x -= x&-x;
return res;
}
}b;
bool cmp1(Node a, Node b) {
return a.x < b.x;
}
bool cmp2(Node a, Node b) {
return a.y < b.y;
}
pii MX(pii a, pii b) {
if(a.fi == b.fi) return {a.fi, a.se+b.se};
else if(a.fi > b.fi) return a;
else return b;
}
void cdq(int l, int r) {
if(l == r) return ;
int m = l+r >> ;
cdq(l, m);
sort(a+l, a+m+, cmp2);
sort(a+m+, a+r+, cmp2);
int p = l, q = m+;
while(q <= r) {
while(p <= m && a[p].y <= a[q].y) b.add(a[p].z, {a[p].len, a[p].cnt}), ++p;
pii t1 = b.mx(a[q].z);
t1.fi++;
pii t2 = MX({a[q].len, a[q].cnt}, t1);
a[q].len = t2.fi, a[q].cnt = t2.se;
++q;
}
for (int i = l; i < p; ++i) b.clr(a[i].z);
sort(a+m+, a+r+, cmp1);
cdq(m+, r);
}
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
vc.clear();
for (int i = ; i <= n; ++i) {
scanf("%d %d %d", &a[i].x, &a[i].y, &a[i].z);
vc.pb(a[i].z);
a[i].len = a[i].cnt = ;
}
sort(vc.begin(), vc.end());
vc.erase(unique(vc.begin(), vc.end()), vc.end());
for (int i = ; i <= n; ++i) a[i].z = lower_bound(vc.begin(), vc.end(), a[i].z) - vc.begin() + ;
sz = (int)vc.size();
sort(a+, a++n, cmp1);
cdq(, n);
pii ans = {, };
for (int i = ; i <= n; ++i) ans = MX(ans, {a[i].len, a[i].cnt});
printf("%d %d\n", ans.fi, ans.se);
}
return ;
}

例题3:HDU - 5324

思路:由于要求字典序最小,所以要求dp[i]:以i为起点的最长上升序列,cdq的话先求右边再求左边

代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
//#define mp make_pair
#define pb push_back
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdi pair<double, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n";
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head const int N = 5e4 + ;
struct Node {
int L, R, id;
}a[N];
vector<int> vc, res;
int sz, n, dp[N], pre[N];
struct BIT {
pii bit[N];
void clr(int x) {
while(x <= sz) bit[x] = {, }, x += x&-x;
}
void add(int x, pii a) {
while(x <= sz) {
if(a.fi > bit[x].fi) bit[x] = a;
else if(a.fi == bit[x].fi && a.se < bit[x].se) bit[x] = a;
x += x&-x;
}
}
pii mx(int x) {
pii res = {, };
while(x) {
if(bit[x].fi > res.fi) res = bit[x];
else if(bit[x].fi == res.fi && bit[x].se < res.se) res = bit[x];
x -= x&-x;
}
return res;
}
}b;
bool cmp1(const Node &a, const Node &b) {
return a.id < b.id;
}
bool cmp2(const Node &a, const Node &b) {
return a.R > b.R;
}
void cdq(int l, int r) {
if(l == r) return ;
int m = l+r >> ;
cdq(m+, r);
sort(a+l, a+m+, cmp2);
sort(a+m+, a+r+, cmp2);
int p = l, q = m+;
while(p <= m) {
while(q <= r && a[q].R >= a[p].R) b.add(a[q].L, {dp[a[q].id], a[q].id}), ++q;
pii P = b.mx(a[p].L);
if(P.fi+ > dp[a[p].id]) {
dp[a[p].id] = P.fi+;
pre[a[p].id] = P.se;
}
else if(P.fi+ == dp[a[p].id] && P.se < pre[a[p].id]) pre[a[p].id] = P.se;
++p;
}
for (int i = m+; i < q; ++i) b.clr(a[i].L);
sort(a+l, a+m+, cmp1);
cdq(l, m);
}
int main() {
while(~scanf("%d", &n)) {
vc.clear();
for (int i = ; i <= n; ++i) scanf("%d", &a[i].L), vc.pb(a[i].L);
for (int i = ; i <= n; ++i) scanf("%d", &a[i].R);
sort(vc.begin(), vc.end());
vc.erase(unique(vc.begin(), vc.end()), vc.end());
sz = (int)vc.size();
for (int i = ; i <= n; ++i) {
a[i].id = i;
dp[i] = ;
pre[i] = ;
a[i].L = lower_bound(vc.begin(), vc.end(), a[i].L) - vc.begin() + ;
}
cdq(, n);
res.clear();
int mx = , pe = ;
res.pb();
for (int i = ; i <= n; ++i) {
if(dp[i] > mx) {
mx = dp[i];
pe = pre[i];
res[] = i;
}
}
printf("%d\n", mx);
while(pe) {
res.pb(pe);
pe = pre[pe];
}
for (int i = ; i < mx; ++i) printf("%d%c", res[i], " \n"[i==mx-]);
}
return ;
}

参考:https://www.cnblogs.com/Sakits/p/7990875.html

整体二分:

1.静态区间第k大

思路:首先对于单个查询我们可以二分答案求解,对于一个二分出来的mid,如果我们能知道小于等于mid的个数,就能判断答案是小于等于mid还是大于mid

整体二分就是将所有查询的二分放在一起考虑,如果对于某个查询我们能快速知道小于等于mid的个数(可以用树状数组维护,将小于等于mid的修改按位置加入),

我们就能知道哪些查询答案是小于等于mid,哪些查询答案是大于mid,哪些修改只对答案小于等于mid的查询有用,哪些修改只对答案大于mid的查询有用

按照这个标准将所有查询修改分成两部分,递归求解,直到二分答案的区间长度为1。具体实现看代码(l,r表示二分答案的区间,L,R表示查询或修改的区间)

时间复杂度:与cdq分治类似,为O(n*log(n)^2)

POJ - 2104

代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
//#define mp make_pair
#define pb push_back
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdi pair<double, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n";
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head const int N = 1.1e5 + ;
const int INF = 0x3f3f3f3f;
struct Node {
int ty, x, y, k, id;
}a[N], t1[N], t2[N];
int n, m, ans[N];
struct BIT {
int bit[N];
inline void add(int x, int a) {
while(x < N) bit[x] += a, x += x&-x;
}
inline int sum(int x) {
int res = ;
while(x) res += bit[x], x -= x&-x;
return res;
}
}b;
void solve(int l, int r, int L, int R) {
if(l > r || L > R) return ;
if(l == r) {
for (int i = L; i <= R; ++i) if(a[i].ty == ) ans[a[i].id] = l;
return ;
}
int m = l+r >> ;
int p = , q = ;
for (int i = L; i <= R; ++i) {
if(a[i].ty == ){
int cnt = b.sum(a[i].y) - b.sum(a[i].x-);
if(cnt >= a[i].k) t1[++p] = a[i];
else a[i].k -= cnt, t2[++q] = a[i];
}
else {
if(a[i].x <= m) b.add(a[i].id, ), t1[++p] = a[i];
else t2[++q] = a[i];
}
}
for (int i = ; i <= p; ++i) if(t1[i].ty == ) b.add(t1[i].id, -);
for (int i = ; i <= p; ++i) a[L+i-] = t1[i];
for (int i = ; i <= q; ++i) a[L+p+i-] = t2[i];
solve(l, m, L, L+p-);
solve(m+, r, L+p, R);
}
int main() {
scanf("%d %d", &n, &m);
for (int i = ; i <= n; ++i) {
scanf("%d", &a[i].x);
a[i].ty = ;
a[i].id = i;
}
for (int i = n+; i <= n+m; ++i) {
scanf("%d %d %d", &a[i].x, &a[i].y, &a[i].k);
a[i].ty = ;
a[i].id = i-n;
}
solve(-INF, INF, , n+m);
for (int i = ; i <= m; ++i) printf("%d\n", ans[i]);
return ;
}

2.动态第k大

思路:在静态第k大的基础上将修改拆成两个,具体看代码

ZOJ - 2112

代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
//#define mp make_pair
#define pb push_back
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdi pair<double, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n";
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head const int N = 1e5 + ;
const int INF = 0x3f3f3f3f;
struct Node {
int ty, x, y, k, id;
}q[N], t1[N], t2[N];
int n, m, a[N], ans[N];
struct BIT {
int bit[N];
inline void add(int x, int a) {
while(x <= n) bit[x] += a, x += x&-x;
}
inline int sum(int x) {
int res = ;
while(x) res += bit[x], x -= x&-x;
return res;
}
}b;
void solve(int l, int r, int L, int R) {
if(l > r || L > R) return ;
if(l == r) {
for (int i = L; i <= R; ++i) if(q[i].ty == ) ans[q[i].id] = l;
return ;
}
int m = l+r >> , c1 = , c2 = ;
for (int i = L; i <= R; ++i) {
if(q[i].ty == ) {
int cnt = b.sum(q[i].y) - b.sum(q[i].x-);
if(q[i].k <= cnt) t1[++c1] = q[i];
else q[i].k -= cnt, t2[++c2] = q[i];
}
else {
if(q[i].x <= m) b.add(q[i].id, q[i].y), t1[++c1] = q[i];
else t2[++c2] = q[i];
}
}
for (int i = ; i <= c1; ++i) if(t1[i].ty == ) b.add(t1[i].id, -t1[i].y);
for (int i = ; i <= c1; ++i) q[L+i-] = t1[i];
for (int i = ; i <= c2; ++i) q[L+c1+i-] = t2[i];
solve(l, m, L, L+c1-);
solve(m+, r, L+c1, R);
}
int T, x, y, k;
char op[];
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d %d", &n, &m);
int tot = ;
for (int i = ; i <= n; ++i) {
scanf("%d", &a[i]);
q[++tot] = {, a[i], , , i};
}
for (int i = ; i <= m; ++i) {
scanf("%s", op);
if(op[] == 'Q') {
scanf("%d %d %d", &x, &y, &k);
q[++tot] = {, x, y, k, i};
}
else {
scanf("%d %d", &x, &y);
q[++tot] = {, a[x], -, , x};
q[++tot] = {, a[x]=y, , , x};
}
ans[i] = -;
}
solve(, INF, , tot);
for (int i = ; i <= m; ++i) if(~ans[i]) printf("%d\n", ans[i]);
}
return ;
}

例题1:HYSBZ - 3110

思路:在线段树上进行区间加法

代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
//#define mp make_pair
#define pb push_back
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdi pair<double, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n";
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head const int N = 5e4 + ;
struct Node {
int ty, x, y, id;
LL c;
}a[N], t1[N], t2[N];
int n, m, ans[N];
LL tree[N<<], lazy[N<<];
inline void push_up(int rt) {
tree[rt] = tree[rt<<] + tree[rt<<|];
}
inline void push_down(int rt, int len) {
lazy[rt<<] += lazy[rt];
lazy[rt<<|] += lazy[rt];
tree[rt<<] += lazy[rt]*(len - (len>>));
tree[rt<<|] += lazy[rt]*(len>>);
lazy[rt] = ;
}
void update(int L, int R, int x, int rt, int l, int r) {
if(L <= l && r <= R) {
lazy[rt] += x;
tree[rt] += x*(r-l+);
return ;
}
if(lazy[rt]) push_down(rt, r-l+);
int m = l+r >> ;
if(L <= m) update(L, R, x, ls);
if(R > m) update(L, R, x, rs);
push_up(rt);
}
LL query(int L, int R, int rt, int l, int r) {
if(L <= l && r <= R) return tree[rt];
if(lazy[rt]) push_down(rt, r-l+);
int m = l+r >> ;
LL ans = ;
if(L <= m) ans += query(L, R, ls);
if(R > m) ans += query(L, R, rs);
push_up(rt);
return ans;
}
void solve(int l, int r, int L, int R) {
if(l > r || L > R) return ;
if(l == r) {
for (int i = L; i <= R; ++i) if(a[i].ty == ) ans[a[i].id] = l;
return ;
}
int m = l+r >> , p = , q = ;
for (int i = L; i <= R; ++i) {
if(a[i].ty == ) {
LL cnt = query(a[i].x, a[i].y, , , n);
if(cnt >= a[i].c) t2[++q] = a[i];
else a[i].c -= cnt, t1[++p] = a[i];
}
else {
if(a[i].c > m) update(a[i].x, a[i].y, , , , n), t2[++q] = a[i];
else t1[++p] = a[i];
}
}
for (int i = ; i <= q; ++i) if(t2[i].ty == ) update(t2[i].x, t2[i].y, -, , , n);
for (int i = ; i <= p; ++i) a[L+i-] = t1[i];
for (int i = ; i <= q; ++i) a[L+p+i-] = t2[i];
solve(l, m, L, L+p-);
solve(m+, r, L+p, R);
}
int tot = ;
int main() {
scanf("%d %d", &n, &m);
for (int i = ; i <= m; ++i) scanf("%d %d %d %lld", &a[i].ty, &a[i].x, &a[i].y, &a[i].c), a[i].id = (a[i].ty == )?i:(++tot);
solve(-n, n, , m);
for (int i = ; i <= tot; ++i) printf("%d\n", ans[i]);
return ;
}

例题2:P1527 [国家集训队]矩阵乘法

思路:在二维树状数组上进行加法

代码:

#include<bits/stdc++.h>
using namespace std;
#define y1 y11
#define LL long long const int N = , M = 3.2e5 + ;
const int INF = 0x3f3f3f3f;
struct Node {
int ty, x1, y1, x2, y2, k, id;
}a[M], t1[M], t2[M];
int n, m, ans[M];
struct BIT2 {
int bit[N][N];
inline void add(int x, int y, int a) {
for (int i = x; i <= n; i += i&-i) {
for (int j = y; j <= n; j += j&-j) {
bit[i][j] += a;
}
}
}
inline int sum(int x, int y) {
int res = ;
for (int i = x; i > ; i -= i&-i) {
for (int j = y; j > ; j -= j&-j) {
res += bit[i][j];
}
}
return res;
}
}B;
void solve(int l, int r, int L, int R){
if(l > r || L > R) return ;
if(l == r) {
for (int i = L; i <= R; ++i) {
if(a[i].ty == ) ans[a[i].id] = l;
}
return ;
}
int m = l+r >> , p = , q = ;
for (int i = L; i <= R; ++i) {
if(a[i].ty == ) {
if(a[i].k <= m) B.add(a[i].x1, a[i].y1, ), t1[++p] = a[i];
else t2[++q] = a[i];
}
else {
int cnt = B.sum(a[i].x2, a[i].y2) - B.sum(a[i].x1-, a[i].y2) - B.sum(a[i].x2, a[i].y1-) + B.sum(a[i].x1-, a[i].y1-);
if(cnt >= a[i].k) t1[++p] = a[i];
else a[i].k -= cnt, t2[++q] = a[i];
}
}
for (int i = ; i <= p; ++i) if(t1[i].ty == ) B.add(t1[i].x1, t1[i].y1, -);
for (int i = ; i <= p; ++i) a[L+i-] = t1[i];
for (int i = ; i <= q; ++i) a[L+p+i-] = t2[i];
solve(l, m, L, L+p-);
solve(m+, r, L+p, R);
}
int main() {
int tot = ;
scanf("%d %d", &n, &m);
for (int i = ; i <= n; ++i) {
for (int j = ; j <= n; ++j) {
++tot;
scanf("%d", &a[tot].k);
a[tot].x1 = i;
a[tot].y1 = j;
a[tot].ty = ;
}
}
for (int i = ; i <= m; ++i) {
++tot;
scanf("%d %d %d %d %d", &a[tot].x1, &a[tot].y1, &a[tot].x2, &a[tot].y2, &a[tot].k);
a[tot].ty = ;
a[tot].id = i;
}
solve(-INF, INF, , tot);
for (int i = ; i <= m; ++i) printf("%d\n", ans[i]);
return ;
}

算法笔记--CDQ分治 && 整体二分的更多相关文章

  1. [学习笔记] CDQ分治&整体二分

    突然诈尸.png 这两个东西好像都是离线骗分大法... 不过其实这两个东西并不是一样的... 虽然代码长得比较像 CDQ分治 基本思想 其实CDQ分治的基本思想挺简单的... 大概思路就是长这样的: ...

  2. 一篇自己都看不懂的CDQ分治&整体二分学习笔记

    作为一个永不咕咕咕的博主,我来更笔记辣qaq CDQ分治 CDQ分治的思想还是比较简单的.它的基本流程是: \(1.\)将所有修改操作和查询操作按照时间顺序并在一起,形成一段序列.显然,会影响查询操作 ...

  3. Cdq分治整体二分学习记录

    这点东西前前后后拖了好几个星期才学会……还是自己太菜啊. Cdq分治的思想是:把问题序列分割成左右两个,先单独处理左边,再处理左边对右边的影响,再单独处理右边.这样可以消去数据结构上的一个log,降低 ...

  4. CDQ分治&整体二分学习个人小结

    目录 小结 CDQ分治 二维LIS 第一道裸题 bzoj1176 Mokia bzoj3262 陌上花开 bzoj 1790 矩形藏宝地 hdu5126四维偏序 P3157 [CQOI2011]动态逆 ...

  5. [学习笔记] CDQ分治 从感性理解到彻底晕菜

    最近学了一种叫做CDQ分治的东西...用于离线处理一系列操作与查询似乎跑得很快233 CDQ的名称似乎源于金牌选手陈丹琦 概述: 对于一坨操作和询问,分成两半,单独处理左半边和处理左半边对于右半边的影 ...

  6. CQD(陈丹琦)分治 & 整体二分——专题小结

    整体二分和CDQ分治 有一些问题很多时间都坑在斜率和凸壳上了么--感觉斜率和凸壳各种搞不懂-- 整体二分 整体二分的资料好像不是很多,我在网上找到了一篇不错的资料:       整体二分是个很神的东西 ...

  7. [学习笔记]CDQ分治和整体二分

    序言 \(CDQ\) 分治和整体二分都是基于分治的思想,把复杂的问题拆分成许多可以简单求的解子问题.但是这两种算法必须离线处理,不能解决一些强制在线的题目.不过如果题目允许离线的话,这两种算法能把在线 ...

  8. CDQ分治 & 整体分治

    Part 1:CDQ分治 CDQ分治讲解博客 可以把CDQ分治理解为类似与归并排序求逆序对个数的一种分治算法(至少我现在是这么想的).先处理完左右两边各自对答案的贡献,在处理跨越左右两边的对答案的贡献 ...

  9. 学习笔记 | CDQ分治

    目录 前言 啥是CDQ啊(它的基本思想) 例题 后记 参考博文 前言 博主太菜了 学习快一年的OI了 好像没有什么会的算法 更寒碜的是 学一样还不精一样TAT 如有什么错误请各位路过的大佬指出啊感谢! ...

随机推荐

  1. django 之(五) --- RESTApi总结

    RESTful django-rest-framework serializers 序列化工具.序列化与反序列化 级联模型 添加级联字段 nested 级联字段的key原来必须就是存在的 隐性属性.自 ...

  2. Session服务器之Memcached与Redis

    安装Memcached[root@nginx ~]# yum -y install libevent memcached 指定用户大小等信息,工作环境中常指定大小一般为4到8G,此信息测试使用.[ro ...

  3. STL标准模板库之set

    目录 一.关联式容器 二.什么是set? 特点 优缺点和适用场景: 三.定义及初始化 四.基本操作 1)容量函数 2)修改函数 3)迭代器 4)其他函数 五.与序列容器的不同点 一.关联式容器 关联式 ...

  4. 利用css 画各种三角形

      #triangle-up {    width: 0;    height: 0;    border-left: 50px solid transparent;    border-right: ...

  5. lua介绍及环境搭建(一)

    一.介绍 1.简介 Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能. 其设计目的是为了嵌入应用程序中,从 ...

  6. 【AtCoder】ARC064

    ARC064 C - Boxes and Candies 先把每个盒子都消到x 然后从前往后推,要求第二个的上界是x-前一个 因为我们要求靠后的那个尽量小,会对后面的修改影响尽量小 #include ...

  7. spring入门一:框架整体简介

    1:spring的基本框架主要包含六大模块:DAO.ORM.AOP.JEE.WEB.CORE DAO:(Data Access Object) 数据访问对象,是一个面向对象的数据库接口. ORM:(O ...

  8. split(".")不生效的问题

    前言:今天用String的split(".")函数分割字符串,结果总是一个空的String数组: 解决:输入的regex是一个正则表达式,很多在正则表达式里面有特殊意义的比如 &q ...

  9. Ural 1298 Knight 题解

    目录 Ural 1298 Knight 题解 题意 题解 程序 Ural 1298 Knight 题解 题意 给定一个\(n\times n(1\le n\le8)\)的国际象棋棋盘和一个骑士(基本上 ...

  10. CSS实现单选按钮

    import React from 'react' import PropTypes from 'prop-types' import CX from 'classnames' import _ fr ...