2815. 三维偏序
题目链接
2815. 三维偏序
给定 \(n\) 个元素(编号 \(1 \sim n\)),其中第 \(i\) 个元素具有 \(a_i,b_i,c_i\) 三种属性。
设 \(f(i)\) 表示满足以下 \(4\) 个条件:
- \(a_j \le a_i\)
- \(b_j \le b_i\)
- \(c_j \le c_i\)
- \(j \neq i\)
的 \(j\) 的数量。
对于 \(d \in [0,n)\),求满足 \(f(i) = d\) 的 \(i\) 的数量。
输入格式
第一行两个整数 \(n,k\),表示元素数量和最大属性值。
接下来 \(n\) 行,其中第 \(i\) 行包含三个整数 \(a_i ,b_i,c_i\),分别表示第 \(i\) 个元素的三个属性值。
输出格式
共 \(n\) 行,每行输出一个整数,其中第 \(d+1\) 行的整数表示满足 \(f(i) = d\) 的 \(i\) 的数量。
数据范围
\(1 \le n \le 10^5\),
\(1 \le a_i,b_i,c_i \le k \le 2 \times 10^5\)
输入样例:
10 3
3 3 3
2 3 3
2 3 1
3 1 1
3 1 2
1 3 1
1 1 2
1 2 2
1 3 2
1 2 1
输出样例:
3
1
3
0
1
0
1
0
0
1
解题思路
cdq分治
cdq分治主要用来解决三维偏序问题,其作用主要是用来以较小的空间复杂度取代某些数据结构
例如本题如果数据范围较小的话可以用二维树状数组来求解,即先将第一个属性排序,然后其他两个属性用二维树状数组求解即可,但本题空间复杂度不允许
本题得用cdq分治求解:先将第一个属性排序,然后在此基础上对第二个属性进行归并排序,同时在第二个属性排序的过程中用树状数组维护第三个属性
- 时间复杂度:\(O(nlog^2n)\)
代码
// Problem: 三维偏序
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2817/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=2e5+5;
int n,k,tr[N],res[N];
struct A
{
int a,b,c,s,res;
bool operator<(const A &o)const
{
if(a!=o.a)return a<o.a;
if(b!=o.b)return b<o.b;
return c<o.c;
}
bool operator==(const A &o)const
{
return a==o.a&&b==o.b&&c==o.c;
}
}a[N],b[N];
void add(int x,int y)
{
for(;x<N;x+=x&-x)tr[x]+=y;
}
int ask(int x)
{
int res=0;
for(;x;x-=x&-x)res+=tr[x];
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(a[i].b<=a[j].b)add(a[i].c,a[i].s),b[++k]=a[i++];
else
a[j].res+=ask(a[j].c),b[++k]=a[j++];
while(i<=mid)add(a[i].c,a[i].s),b[++k]=a[i++];
while(j<=r)a[j].res+=ask(a[j].c),b[++k]=a[j++];
for(int i=l;i<=mid;i++)add(a[i].c,-a[i].s);
for(int i=l,j=1;j<=k;j++,i++)a[i]=b[j];
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>a[i].a>>a[i].b>>a[i].c,a[i].s=1;
sort(a+1,a+1+n);
int k=2;
for(int i=2;i<=n;i++)
if(a[i]==a[k-1])a[k-1].s++;
else
a[k++]=a[i];
merge_sort(1,k-1);
for(int i=1;i<k;i++)res[a[i].res+a[i].s-1]+=a[i].s;
for(int i=0;i<n;i++)cout<<res[i]<<'\n';
return 0;
}