把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【题解】P3810 【模板】三维偏序(陌上花开)- $CDQ$ - 三维偏序

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

声明:本博客所有题解都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。

题目描述

\(n\) 个元素,第 \(i\) 个元素有 \(a_i,b_i,c_i\) 三个属性,设 \(f(i)\) 表示满足 \(a_j \leq a_i\)\(b_j \leq b_i\)\(c_j \leq c_i\)\(j \ne i\)\(j\) 的数量。

对于 \(d \in [0, n)\),求 \(f(i) = d\) 的数量。

\(\\\)


\(\\\)

\(Solution\)

这道题和树状数组那道一样

这里的 \(a\) 可以看做树状数组里的时间,只不过那里的操作是有序的,所以这道题第一步是把 \(a\) 排个序,当做 \(CDQ\) 的时间

然后 \(b\) 和那道题的位置一样,于是也可以归并排序balabala

但是还有一个 \(c\) ,我们可以用一个树状数组维护,累计答案就可!

完结

然而这道题有非常多的坑点...

\(1.\)对于相等的三元组,要把它们合并,并且要存它们的数量,在树状数组累计的时候加的应该是那个三元组的数量,最后统计答案,每个三元组还得加上数量 \(-1\) ,因为相等也算答案(关于为什么树状数组不用判相等,因为每个操作的时间是唯一的,所以不可能有相等的)

\(2.\)排序的时候如果 \(a\) 不相等则按 \(a\) 的大小排,如果 \(a\) 相等再按 \(b\) 的大小排,还相等再按 \(c\) 的大小排,不然计算答案会出错

\(3.\)由于每次都要清空树状数组,虽然照理来说是跑满 \(O(n\log{n})\)\(10^7\) 可以过来着,但是被卡了...重新开一个数组记录被修改过的树状数组,清空就快一些

完结撒花✿✿ヽ(°▽°)ノ✿

\(\\\)


\(\\\)

\(Code\)

#include<bits/stdc++.h>
#define mid ((l + r) >> 1)
#define lowbit(x) x & -x
#define F(i, x, y) for(int i = x; i <= y; ++ i)
using namespace std;
int read();
const int N = 1e5 + 5;
const int M = 2e5 + 5;
int n, k, m, cnt;
int tree[M], f[N], d[N], s[N], vis[M];
struct node{
	int a, b, c, num, w;
}p[N], tmp[N];
bool cmp(node x, node y)
{
	if(x.a != y.a) return x.a < y.a;
	if(x.b != y.b) return x.b < y.b;
	return x.c < y.c;
}
void add(int pos, int v){for(; pos <= m; pos += lowbit(pos)) {tree[pos] += v; if(! vis[pos]) s[++ s[0]] = pos, vis[pos] = 1;}}
int getans(int pos){int res = 0; for(; pos; pos -= lowbit(pos)) res += tree[pos]; return res;}
void CDQ(int l, int r)
{
	if(l == r) return;
	CDQ(l, mid), CDQ(mid + 1, r);
	int i = l, j = mid + 1, o = l - 1;
	while(i <= mid && j <= r)
		if(p[i].b < p[j].b || p[i].b == p[j].b && p[i].c <= p[j].c)
			add(p[i].c, p[i].w), tmp[++ o] = p[i ++];
		else f[p[j].num] += getans(p[j].c), tmp[++ o] = p[j ++];
	while(i <= mid) tmp[++ o] = p[i ++];
	while(j <= r) f[p[j].num] += getans(p[j].c), tmp[++ o] = p[j ++];
	F(i, 1, s[0]) tree[s[i]] = 0, vis[s[i]] = 0; s[0] = 0;
	F(i, l, r) p[i] = tmp[i];
}
int main()
{
	n = read(), m = read();
	F(i, 1, n) p[i].a = read(), p[i].b = read(), p[i].c = read();
	sort(p + 1, p + 1 + n, cmp);
	F(i, 1, n) 
	{
		++ k;
		if(p[i].a != p[i + 1].a || p[i].b != p[i + 1].b || p[i].c != p[i + 1].c)
			tmp[++ cnt] = p[i], tmp[cnt].num = cnt, tmp[cnt].w = k, k = 0;
	}
	F(i, 1, cnt) p[i] = tmp[i];
	CDQ(1, cnt);
	F(i, 1, cnt) d[f[p[i].num] + p[i].w - 1] += p[i].w;
	F(i, 0, n - 1) printf("%d\n", d[i]);
	return 0;
}
int read()
{
	int x = 0;
	char c = getchar();
	while(c < '0' || c > '9') c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x;
}
posted @ 2020-05-07 21:02  Bn_ff  阅读(163)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end