陌上花开,可缓缓归矣 —— 吴越王
P3810 【模板】三维偏序(陌上花开)
# 二维偏序 - 归并排序求逆序对
P1908 逆序对
直接扔个板子好了:
#include <cstdio> | |
#include <iostream> | |
#define rep(i,s,t) for(int i=s;i<=t;i++) | |
#define mid (l+r)>>1 | |
using namespace std; | |
using ll = long long; | |
const int MAXN = 5e5 + 233; | |
int n, a[MAXN], t[MAXN]; | |
ll ans; | |
void mergeSort(int l, int r) | |
{ | |
if (l == r) | |
return; | |
int m = mid; | |
mergeSort(l, m); | |
mergeSort(m + 1 , r); | |
int p = l, q = m + 1, s = l; | |
while (p <= m && q <= r) | |
if (a[p] > a[q]) | |
{ | |
t[s++] = a[q++]; | |
ans += m - p + 1; | |
} | |
else | |
t[s++] = a[p++]; | |
while (p <= m) | |
t[s++] = a[p++]; | |
while (q <= r) | |
t[s++] = a[q++]; | |
rep (i, l, r) | |
a[i] = t[i]; | |
} | |
int main() | |
{ | |
scanf("%d", &n); | |
rep (i, 1, n) | |
scanf("%d", &a[i]); | |
mergeSort(1, n); | |
printf("%lld\n", ans); | |
return 0; | |
} |
# 三维偏序 - CDQ 分治 + BIT
# CDQ 分治
一种思想,就分治嘛..(跟线段树啊,归并排序啊什么的都有点像 emm)
Fake Code:
void cdq(int l, int r) | |
{ | |
if (l == r) return; | |
int m = (l + r) >> 1; | |
cdq(l, m); cdq(m + 1, r); | |
/** | |
* 做些什么合并左右区间 | |
*/ | |
return; | |
} |
# 陌上花开
# 题目描述
三维偏序经典问题:
竟然找不到原题面了真可惜 qwq,,算法竞赛选手不配学语文呜呜呜咱最会爬了 QAQ
题目大意:
有一堆花,每朵花有三个属性 。
对于每一朵花 ,认为它比另一朵花 漂亮,当且仅当 。
每朵花 有一个等级,一朵花的等级是它的美丽 能超过的花的数量。
求每个等级的花有多少。
# 输入输出格式
输入 n, k 表示花朵数和最大属性值,接下来每行是一朵花的 a, b, c 三个属性。
n 范围 1e5, k 范围 2e5。
分行输出 0 ~ n - 1 等级花朵个数。
# 思路
先按 a 排序,再拿这组数据对 b 做归并排序, 合并的时候用树状数组维护一下 c,查询记录答案即可。
由于多次处理重复元素会出事,提前进行一下去重,记录重复次数什么的 emm
# 代码
#include <cstdio> | |
#include <iostream> | |
#include <algorithm> | |
#define rep(i,s,t) for(int i=s;i<=t;i++) | |
#define mid (l+r)>>1 | |
using namespace std; | |
const int MAXN = 1e5 + 233; | |
int n, k, N; | |
struct node | |
{ | |
int a, b, c; | |
int cnt, ans; // 重复次数,等级(不含相同的花) | |
} arr[MAXN], tmparr[MAXN]; | |
int tre[MAXN << 1], ans[MAXN]; | |
bool operator == (node& xx, node& yy) | |
{ | |
return (yy.a == xx.a) && (yy.b == xx.b) && (yy.c == xx.c); | |
} | |
int lb(int x) | |
{ | |
return x & (-x); | |
} | |
void add(int x, int v) | |
{ | |
for (; x <= k; x += lb(x)) | |
tre[x] += v; | |
} | |
int query(int x) | |
{ | |
int sum = 0; | |
for (; x; x -= lb(x)) | |
sum += tre[x]; | |
return sum; | |
} | |
bool cmp1(node& xx, node& yy) | |
{ | |
return xx.a != yy.a ? xx.a < yy.a : xx.b != yy.b ? xx.b < yy.b : xx.c < yy.c; | |
} | |
void cdq(int l, int r) | |
{ | |
if (l == r) return; | |
int m = mid; | |
cdq(l, m); | |
cdq(m + 1, r); | |
int p = l, q = m + 1, s = l; | |
while (p <= m && q <= r) | |
{ | |
if (arr[p].b <= arr[q].b) | |
{ | |
add(arr[p].c, arr[p].cnt); //b 条件符合,把 c 扔进树状数组里统计 | |
tmparr[s++] = arr[p++]; | |
} | |
else | |
{ | |
arr[q].ans += query(arr[q].c); //b 条件符合,从树状数组里查询 c | |
tmparr[s++] = arr[q++]; | |
} | |
} | |
while (q <= r) | |
{ | |
arr[q].ans += query(arr[q].c); | |
tmparr[s++] = arr[q++]; | |
} | |
rep (i, l, p - 1) | |
add(arr[i].c, -arr[i].cnt); // 记得清空树状数组 | |
while (p <= m) | |
tmparr[s++] = arr[p++]; | |
rep (i, l, r) | |
arr[i] = tmparr[i]; | |
} | |
int main() | |
{ | |
scanf("%d%d", &n, &k); | |
rep (i, 1, n) | |
scanf("%d%d%d", &tmparr[i].a, &tmparr[i].b, &tmparr[i].c); | |
sort(tmparr + 1, tmparr + n + 1, cmp1); | |
// 去重 | |
rep (i, 1, n) | |
{ | |
int j = i + 1; | |
while (tmparr[j] == tmparr[j - 1] && j <= n) | |
j++; | |
arr[++N] = tmparr[i]; | |
arr[N].cnt = j - i; | |
i = j - 1; // 这里炸掉一次 qwq | |
} | |
cdq(1, N); // 这里炸掉一次,是 N 不是 n,qwq | |
rep (i, 1, N) | |
//ans [等级] += 重复次数,记录每个等级花朵个数 | |
// 相同的花,每一朵都比其它相同的要漂亮,所以每一朵的等级都要加上 cnt - 1 | |
ans[arr[i].ans + arr[i].cnt - 1] += arr[i].cnt; | |
rep (i, 0, n - 1) | |
printf("%d\n", ans[i]); | |
return 0; | |
} |