二维三维偏序

一维偏序::就是排序,
二维偏序:排序+树状数组:

例题: 给定 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值