题意

$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$ 的数量。

$1 \leq n \leq 10^5,1 \leq a_i, b_i, c_i \le k \leq 2 \times 10^5$

题解

这里用 CDQ 分治。

对于每个点,先按 $a$ 为关键字排序。然后分治。

在分治的过程中,前半段的 $a$ 一定小于等于后半段 $a$ ,那就无忌惮地再按 $b$ 排序(此时已经变成二维偏序了),然后树状数组维护 $c$

代码

const int N = 1e5 + 10, K = 2e5 + 10;

int n, m, k;
struct node {
	int a, b, c, cnt, ans;
	bool operator < (const node &x) const {
		return a < x.a || (a == x.a && (b < x.b || (b == x.b && (c < x.c))));
	}
}a[N], b[N];
int t[K];

void modify(int x, int val) {for (; x <= K - 10; x += x & -x) t[x] += val;}
int query (int x) {int ans = 0; for (; x; x -= x & -x) ans += t[x]; return ans;} 

int ans[N];

bool cmp (node a, node b){return a.b < b.b || (a.b == b.b && (a.c < b.c));}

void CDQ (int l, int r) {
	if (l == r) return;
	int mid = (l + r) >> 1;
	CDQ (l, mid), CDQ (mid + 1, r);
	sort (a + l, a + 1 + mid, cmp), sort (a + 1 + mid, a + 1 + r, cmp);
	int i, j = l;
	for (int i = mid + 1; i <= r; i ++) {
		while (a[i].b >= a[j].b && j <= mid) {
			modify (a[j].c, a[j].cnt);
			j++;
		}
		a[i].ans += query(a[i].c);
	}
	for (i = l; i < j; i++) modify(a[i].c, -a[i].cnt);
}

int main() {
//	freopen(".in", "r", stdin);
//	freopen(".out", "w", stdout);
	scanf ("%d%d", &n, &k);
	for (int i = 1; i <= n; i++)
		scanf ("%d%d%d", &b[i].a, &b[i].b, &b[i].c);
	sort (b + 1, b + 1 + n);
	for (int i = 1, cnt = 0; i <= n; i++) {
		cnt ++;
//		if (b[i] != b[i + 1])
		if (b[i].a != b[i + 1].a || b[i].b != b[i + 1].b || b[i].c != b[i + 1].c) {
			a[++m] = b[i];
			a[m].cnt = cnt;
			cnt = 0;
		}
	}
	CDQ (1, m);
	for (int i = 1; i <= m; i++)
		ans[a[i].cnt + a[i].ans - 1] += a[i].cnt;
	for (int i = 0; i < n; i++)
		printf ("%d\n", ans[i]);
    return 0;
}
EOF

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。

博客信息

作者
Jayun
时间
2023-11-11 07:25:02