参考: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. JavaScript中new运算符的实现

    废话不多说直接进入正题,首先我们需要先知道new运算符到底做了哪些事情,再来模拟它实现这一功能. 1. 建立一个空的Object对象: 2. 把这个空对象用__proto__链接到原型 3. 用app ...

  2. 【计算机视觉】opencv读取多个摄像头

    [计算机视觉]opencv读取多个摄像头 标签(空格分隔): [图像处理] 说明:今天蹭了机器视觉课程,讲到了stereopsis,立体视觉,讲到了关于通过多个摄像头获取object的depth信息的 ...

  3. IIS 6.0的web园 最大工作进程数细谈

    这篇文章主要介绍了IIS 6.0的web园 最大工作进程数,需要的朋友可以参考下:(摘自:http://www.jb51.net/article/84817.htm) IIS 6.0允许将应用程序池配 ...

  4. Node async 控制代码执行顺序

    当你有一个集合,你想循环集合,然后对每个集合按照顺序执行相应的方法你可以使用forEachSeries

  5. java.net.UnknownHostException: MySQLMASTER: MySQLMASTER: 未知的名称或服务

    linux环境在连接Activemq的时候报以下信息,找了半天配了下host  OK了,记录一下. java.net.UnknownHostException: MySQLMASTER: MySQLM ...

  6. Oracle表级约束和列级约束

    Oracle表级约束和列级约束 1. 表级定义约束 指的是在定义完一个表所有列之后,再去定义所有相关的约束. 注意:not null 约束只能在列级上定义. 2. 列级定义约束 指的是在定义一个表的每 ...

  7. 【洛谷】P5348 密码解锁

    [洛谷]P5348 密码解锁 很显然我们可以推导出这个式子 设\(a(m)\)为\(m\)位置的值 \[ \mu(m) = \sum_{m | d} a(d) \\ a(m) = \sum_{m|d} ...

  8. idea配置glassFish启动项目

    将项目打成war包形式. 然后配置glassFish +号配置启动包 此处配置启动路径 ........ 路径也可以在localhost:4848 --->中的Applicaton中去配置

  9. 网络编程[第三篇]基于tcp协议实现远程连接

    需要用到subprogress模块来远程控制cmd控制台程序来得到控制台的输出信息 一.服务端 —— 控制输出信息 import socket import subprocess #socket实例化 ...

  10. glang flag

    package main import ( "flag" "fmt" "github.com/golang/glog" ) /* 解析 fl ...