CDQ分治
即对于每个i,有多少个j使得 a j < = a i a_j<=a_i aj<=ai, b j < = b i b_j<=b_i bj<=bi, c j < = c i c_j<=c_i cj<=ci
一维:
排序即可
二维:
①:树状数组解决即可
值域统计后插入元素即可
②:分治
加双关键字排序(便于处理 a j < = a i , b j < = b i a_j<=a_i,b_j<=b_i aj<=ai,bj<=bi)的情况
e g eg eg 逆序对:
①:i,j都在左半边
②:i,j都在右半边
③:j在左,i在右
①和②递归即可,至于③,考虑满足第二维的限制即可,使用双指针算法,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
三维
①:三关键字排序
②:将满足i,j要求的数对分成三大类
A:i,j均在左半边(递归求)
B:i,j均在右半边(递归求)
C:j在左,i在右
按B排序
对 于 每 个 i , 通 过 双 指 针 找 到 b i < = b j 的 位 置 , 则 在 1 至 j 的 所 有 数 里 找 到 满 足 < = c i 的 数 的 个 数 ( 树 状 数 组 ) 对于每个i, 通过双指针找到bi<=bj的位置, 则在1至j的所有数里找到满足<=c_i的数的个数(树状数组) 对于每个i,通过双指针找到bi<=bj的位置,则在1至j的所有数里找到满足<=ci的数的个数(树状数组)
时间复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
备注:对于 a i = = a j , b i = = b j , c i = = c j a_i==a_j,b_i == b_j,c_i == c_j ai==aj,bi==bj,ci==cj的情况,去除掉,并记录个数,最后在答案加上(个数-1)
模板题
代码+详细注释
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010, M = 200010;
int n, m;
struct Data
{
int a, b, c, s, res;
bool operator< (const Data& t) const
{
if (a != t.a) return a < t.a;
if (b != t.b) return b < t.b;
return c < t.c;
}
bool operator== (const Data& t) const
{
return a == t.a && b == t.b && c == t.c;
}
}q[N], w[N];
int tr[M], ans[N];
int lowbit(int x)
{
return x & -x;
}
void add(int x, int v)
{
for (int i = x; i < M; i += lowbit(i)) tr[i] += v;
}
int query(int x)
{
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
void merge_sort(int l, int r)
{
if (l >= r) return;
int mid = l + r >> 1;
merge_sort(l, mid), merge_sort(mid + 1, r);
int i = l, j = mid + 1, k = 0;
while (i <= mid && j <= r)
if (q[i].b <= q[j].b) add(q[i].c, q[i].s), w[k ++ ] = q[i ++ ];
else q[j].res += query(q[j].c), w[k ++ ] = q[j ++ ];
while (i <= mid) add(q[i].c, q[i].s), w[k ++ ] = q[i ++ ];
while (j <= r) q[j].res += query(q[j].c), w[k ++ ] = q[j ++ ];
for (i = l; i <= mid; i ++ ) add(q[i].c, -q[i].s);
for (i = l, j = 0; j < k; i ++, j ++ ) q[i] = w[j];
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i ++ )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
q[i] = {a, b, c, 1};
}
sort(q, q + n);
int k = 1;
for (int i = 1; i < n; i ++ )
if (q[i] == q[k - 1]) q[k - 1].s ++ ;
else q[k ++ ] = q[i];
merge_sort(0, k - 1);
for (int i = 0; i < k; i ++ )
ans[q[i].res + q[i].s - 1] += q[i].s;
for (int i = 0; i < n; i ++ ) printf("%d\n", ans[i]);
return 0;
}
应用
一:老C的任务
思路:考虑转化为CDQ分治
这对于每个点,求出其作为左上顶点的矩形中所包含的点的权值之和。(即二维前缀和)
而zi只有1,0两种取值,不需使用树状数组,使用一个变量维护即可。
代码如下:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 500010;
int n, m;
struct Data
{
int x, y, z, p, id, sign;
LL sum;
bool operator< (const Data& t) const
{
if (x != t.x) return x < t.x;
if (y != t.y) return y < t.y;
return z < t.z;
}
}q[N], w[N];
LL ans[N];
void merge_sort(int l, int r)
{
if (l >= r) return;
int mid = l + r >> 1;
merge_sort(l, mid), merge_sort(mid + 1, r);
int i = l, j = mid + 1, k = 0;
LL sum = 0;
while (i <= mid && j <= r)
if (q[i].y <= q[j].y) sum += !q[i].z * q[i].p, w[k ++ ] = q[i ++ ];
else q[j].sum += sum, w[k ++ ] = q[j ++ ];
while (i <= mid) sum += !q[i].z * q[i].p, w[k ++ ] = q[i ++ ];
while (j <= r) q[j].sum += sum, w[k ++ ] = q[j ++ ];
for (i = l, j = 0; j < k; i ++, j ++ ) q[i] = w[j];
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i ++ )
{
int x, y, p;
scanf("%d%d%d", &x, &y, &p);
q[i] = {x, y, 0, p};
}
int k = n;
for (int i = 1; i <= m; i ++ )
{
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
q[k ++ ] = {x2, y2, 1, 0, i, 1};
q[k ++ ] = {x1 - 1, y2, 1, 0, i, -1};
q[k ++ ] = {x2, y1 - 1, 1, 0, i, -1};
q[k ++ ] = {x1 - 1, y1 - 1, 1, 0, i, 1};
}
sort(q, q + k);
merge_sort(0, k - 1);
for (int i = 0; i < k; i ++ )
if (q[i].z)
ans[q[i].id] += q[i].sum * q[i].sign;
for (int i = 1; i <= m; i ++ ) printf("%lld\n", ans[i]);
return 0;
}
二:动态逆序对
思路:现在已经有两维,第三维为时间戳,即当前该数被删除的时间
需要记录
代码如下:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100010;
int n, m;
struct Data
{
int a, t, res;
}q[N], w[N];
int tr[N], pos[N];
LL ans[N];
int lowbit(int x)
{
return x & -x;
}
void add(int x, int v)
{
for (int i = x; i < N; i += lowbit(i)) tr[i] += v;
}
int query(int x)
{
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
void merge_sort(int l, int r)
{
if (l >= r) return;
int mid = l + r >> 1;
merge_sort(l, mid), merge_sort(mid + 1, r);
int i = mid, j = r;
while (i >= l && j > mid)
if (q[i].a > q[j].a) add(q[i].t, 1), i -- ;
else q[j].res += query(q[j].t - 1), j -- ;
while (j > mid) q[j].res += query(q[j].t - 1), j -- ;
for (int k = i + 1; k <= mid; k ++ ) add(q[k].t, -1);
j = l, i = mid + 1;
while (j <= mid && i <= r)
if (q[i].a < q[j].a) add(q[i].t, 1), i ++ ;
else q[j].res += query(q[j].t - 1), j ++ ;
while (j <= mid) q[j].res += query(q[j].t - 1), j ++ ;
for (int k = mid + 1; k < i; k ++ ) add(q[k].t, -1);
i = l, j = mid + 1;
int k = 0;
while (i <= mid && j <= r)
if (q[i].a <= q[j].a) w[k ++ ] = q[i ++ ];
else w[k ++ ] = q[j ++ ];
while (i <= mid) w[k ++ ] = q[i ++ ];
while (j <= r) w[k ++ ] = q[j ++ ];
for (i = l, j = 0; j < k; i ++, j ++ ) q[i] = w[j];
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i ++ )
{
scanf("%d", &q[i].a);
pos[q[i].a] = i;
}
for (int i = 0, j = n; i < m; i ++ )
{
int a;
scanf("%d", &a);
q[pos[a]].t = j -- ;
pos[a] = -1;
}
for (int i = 1, j = n - m; i <= n; i ++ )
if (pos[i] != -1)
q[pos[i]].t = j -- ;
merge_sort(0, n - 1);
for (int i = 0; i < n; i ++ ) ans[q[i].t] = q[i].res;
for (int i = 2; i <= n; i ++ ) ans[i] += ans[i - 1];
for (int i = 0, j = n; i < m; i ++, j -- ) printf("%lld\n", ans[j]);
return 0;
}
引用文献
希望做一点微小的贡献