演算法學習-CDQ分治

MafuyuQWQ發表於2024-09-19

對於二維偏序,為普通的求逆序對,只需要先排序一遍,然後樹狀陣列或雙指標即可
而三位偏序甚至更高,則需要用CDQ分治,簡單來說,就是將樹狀陣列和雙指標結合
操作步驟如下:

  • 1.開始將陣列按第一維排序,保證滿足x性質
  • 2.在歸併中,將左右分別按y排序,因為開始以x排序,所以保證了正確性,保證貢獻從左到右
  • 3.相當於用樹狀陣列處理二維偏序,再用雙指標合併左右兩部分陣列即可
#include <bits/stdc++.h>
#define int long long

using namespace std;

const int N = 200010;

int n, k;
struct rua
{
	int x, y, z;
	int res, cnt;
} a[N];
int tr[N], res[N];

bool check(int x, int y)
{
	return (a[x].x == a[y].x && a[x].y == a[y].y && a[x].z == a[y].z);
}

bool cmpx(rua x, rua y)
{
	if (x.x != y.x) return x.x < y.x;
	else if (x.y != y.y) return x.y < y.y;
	return x.z < y.z;
}

bool cmpy(rua x, rua y)
{
	if (x.y != y.y) return x.y < y.y;
	return x.z < y.z;
}

int lowbit(int x)
{
	return x & -x;
}

void update(int x, int v)
{
	for (int i = x; i <= k; 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 solve(int l, int r)
{
	if (l == r) return;
	int mid = l + r >> 1;
	solve(l, mid), solve(mid + 1, r);
	sort(a + l, a + mid + 1, cmpy);
	sort(a + mid + 1, a + r + 1, cmpy);
	int i = mid + 1, j = l;
	while (i <= r)
	{
		while (a[j].y <= a[i].y && j <= mid) update(a[j].z, a[j].cnt), j ++ ;
		a[i].res += query(a[i].z);
		i ++ ;
	}
	for (int i = l; i < j; i ++ ) update(a[i].z, -a[i].cnt);
}

signed main()
{
	cin >> n >> k;
	for (int i = 1; i <= n; i ++ ) cin >> a[i].x >> a[i].y >> a[i].z;
	
	sort(a + 1, a + 1 + n, cmpx);
//	for (int i = 1; i <= n; i ++ ) cout << a[i].x << ' ' << a[i].y << ' ' << a[i].z << '\n';
	int cnt = 0;
	for (int i = 1; i <= n; i ++ )
	{
		int j = i + 1;
		while (check(i, j)) j ++ ;
		j -- ;
		a[ ++ cnt] = {a[i].x, a[i].y, a[i].z, 0, j - i + 1};
		i = j;
	}
//	for (int i = 1; i <= n; i ++ )
//		cout << a[i].cnt << ' ';
//	cout << '\n';
	
	solve(1, n);
	
	for (int i = 1; i <= n; i ++ ) res[a[i].res + a[i].cnt - 1] += a[i].cnt;
	
	for (int i = 0; i < n; i ++ ) cout << res[i] << '\n';
	
	return 0;
}

相關文章