一维偏序::就是排序,
二维偏序:排序+树状数组:
例题: 给定 n 个点(x,y),定义每个点的等级是在该点左下方(含正左、正下)的点的数目,试统计每个等级有多少个点。
题目链接
分析:对于二维偏序:对1维x进行排序,那么在右边的点不会对左边的点有贡献。然后对y建立树状数组。计算0-y的和,就是sum[y];
#include <bits/stdc++.h>
using namespace std;
const int maxn=3.2*10005;
struct node
{
int x,y;
}a[maxn];int c[maxn];int n,cnt[maxn];
int maxv=0;
int cmp(node x,node y)
{
if(x.x!=y.x) return x.x<y.x;
return x.y<y.y;
}
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int val)
{
while(x<=maxv)
{
c[x]+=val;
x+=lowbit(x);
}
}
int sum(int x)
{
int s=0;
while(x>0)
{
s+=c[x];
x-=lowbit(x);
}
return s;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
a[i].x+=1;a[i].y+=1;
maxv=max(maxv,a[i].y);
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++)
{
cnt[sum(a[i].y)]++;
add(a[i].y,1);
}
for(int i=0;i<n;i++) cout<<cnt[i]<<endl;
return 0;
}
三维偏序
首先对一维x进行排序,还是右边的值对左边的值没有贡献,在x是排序好的基础上,将n分为两半,那么左边中y小的一定对右边里y大的有贡献,然后用树状数组来计算累计。
#include <bits/stdc++.h>
using namespace std;
int _n,k;
const int maxn=200005;
struct node
{
int x,y,z,ans,w;
}a[maxn],b[maxn];int n;int c[maxn];
int ans[maxn];
int cmp(node x,node y)
{
if(x.x!=y.x) return x.x<y.x;
if(x.y!=y.y) return x.y<y.y;
return x.z<y.z;
}
int cmp1(node x,node y)
{
if(x.y!=y.y) return x.y<y.y;
if(x.z!=y.z) return x.z<y.z;
return x.x<y.x;
}
int lowbit(int x) {return x&(-x);}
void add(int x,int val)
{
while(x<=k)
{
c[x]+=val;
x+=lowbit(x);
}
}
int sum(int x)
{
int s=0;
while(x>0)
{
s+=c[x];
x-=lowbit(x);
}
return s;
}
void solve(int l,int r)
{
if(l==r) return;
int mid=(l+r)/2;
solve(l,mid);solve(mid+1,r);
sort(a+l,a+mid+1,cmp1);sort(a+mid+1,a+r+1,cmp1);
int i=mid+1;int j=l;
for(;i<=r;i++)//i代表mid右边的,j代表的是mid左边的,
{
//左边中y小的对右边的一定有贡献;
while(a[j].y<=a[i].y&&j<=mid) add(a[j].z,a[j].w),j++;
a[i].ans+=sum(a[i].z);
}
for(i=l;i<j;i++)
{
add(a[i].z,-a[i].w);//去掉痕迹!
}
}
int main()
{
scanf("%d%d",&_n,&k);
for(int i=1;i<=_n;i++) scanf("%d%d%d",&b[i].x,&b[i].y,&b[i].z);
sort(b+1,b+1+_n,cmp);int tot=0;
n=0;
for(int i=1;i<=_n;i++)
{
tot++;
if(b[i].x!=b[i+1].x||b[i].y!=b[i+1].y||b[i].z!=b[i+1].z)
a[++n]=b[i],a[n].w=tot,tot=0;
//这个是避免有x,y,z都一样,a[i].w--就是权值这个坐标有几个。,
}
solve(1,n);
for(int i=1;i<=n;i++)
ans[a[i].ans+a[i].w-1]+=a[i].w;
//为什么再加一遍呢,a[i]--和b[i]不是一一对应的,a[]中是将重复的合并了。
for(int i=0;i<_n;i++) cout<<ans[i]<<endl;
return 0;
}