hdoj 5618 Jam's problem again题解cdq分治解决三维偏序问题

2 篇文章 0 订阅
2 篇文章 0 订阅

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5324
题意:t组数据,每组给出一个n,然后跟着n个点,每个点都有三个值x,y,z,最后输出对于每个点比它三维小于等于的点有多少个。


思路:是一道先一维排序再cdq+树状数组解决三维偏序的入门题,先对x排序,再对y层次cdq分治,对z的排序运用树状数组求逆序对的方式。
代码很长但是感觉思路还是很清晰的,写着有一种蛮舒服的感觉= =

AC代码:

#include<bits/stdc++.h>
#define INF 0x3F3F3F3F
#define endl '\n'
#define pb push_back
#define css(n) cout<<setiosflags(ios::fixed)<<setprecision(n); 
#define sd(a) scanf("%d",&a)
#define sld(a) scanf("%lld",&a)
#define m(a,b) memset(a,b,sizeof a)
#define p_queue priority_queue
using namespace std;
typedef long long ll;
const int maxn=100005;
int n,m;
int t;
double a,b;
int N;
int tre[maxn];
struct node
{
	int x,y,z;
	int sum;
	int id;
}dian[maxn];
int fin[maxn];
bool comp1(node a,node b)
{
	if(a.x!=b.x) return a.x<b.x;
	else
	{
		if(a.y!=b.y) return a.y<b.y;
		else return a.z<b.z;
	}
}
bool comp2(node a,node b)
{
	return a.y<b.y;
}
int lowbit(int x)
{
	return x&(-x);
}
void add(int pos,int val)
{
	while(pos<=N)
	{
		tre[pos]+=val;
		pos+=lowbit(pos);
	}
}
int ask(int x)
{
	int ans=0;
	while(x)
	{
		ans+=tre[x];
		x=x-lowbit(x);
	}
	return ans;
}
void cdq(int l,int r)
{
	if(l==r) return;
	int mid=(l+r)>>1;
	cdq(l,mid),cdq(mid+1,r);//cdq分治,一层一层的分下去,类似于线段树一样。 
	sort(dian+l,dian+mid+1,comp2);
	sort(dian+mid+1,dian+r+1,comp2);//对左边和右边的y排序。 
	int j=l;
	for(int i=mid+1;i<=r;i++)
	{
		for(;j<=mid&&dian[j].y<=dian[i].y;j++)
		{
		//	if(dian[j].y<=dian[i].y)
		    add(dian[j].z,1);//满足左边比右边的y小时就把z加入到树状数组。
			//只要一次j不满足就直接break掉,因为后面的j都比这个j大,也不可能满足小于关系。 
		//	else break;
		}
		dian[i].sum+=ask(dian[i].z);
		
	//	cout<<dian[i].id<<"--"<<dian[i].sum<<endl; 
		
	}
	for(j=j-1;j>=l;j--)
	{
		add(dian[j].z,-1);
	}
	//将j前面加过的树状数组的值还原。 
}
int main()
{
	sd(t);
	while(t--)
	{
		memset(tre,0,sizeof(tre));
		sd(n);
		N=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d%d",&dian[i].x,&dian[i].y,&dian[i].z);
			dian[i].sum=0;
			dian[i].id=i;
			N=max(N,dian[i].z);//树状数组的最大值应该是所有点中z最大的那个值,不优化的话就直接写maxn-1也行,优化后发现也就快了几十ms。。 
		}
		sort(dian+1,dian+1+n,comp1);
		
	/*	for(int i=1;i<=n;i++)
		{
			cout<<dian[i].id<<endl;
		}*/
		
		cdq(1,n);
		for(int i=1;i<=n;)
		{
			int j=i+1;
			int mx=dian[i].sum;
			for(;j<=n&&dian[i].x==dian[j].x&&dian[i].y==dian[j].y&&dian[i].z==dian[j].z;j++)
			{
				mx=max(mx,dian[j].sum);
			}
			for(int k=i;k<j;k++)
			{
				fin[dian[k].id]=mx;
			}
			i=j;
		}
		for(int i=1;i<=n;i++)
		{
			printf("%d\n",fin[i]);
		}
	 } 
	 
	return 0;
} 

修改之后的代码:
解决问题应该是没错的,但是会t掉,,,我也不知道为什么,可能bug不是在去重那儿吗。。。

#include<bits/stdc++.h>
#define INF 0x3F3F3F3F
#define endl '\n'
#define pb push_back
#define css(n) cout<<setiosflags(ios::fixed)<<setprecision(n); 
#define sd(a) scanf("%d",&a)
#define sld(a) scanf("%lld",&a)
#define m(a,b) memset(a,b,sizeof a)
#define p_queue priority_queue
using namespace std;
typedef long long ll;
const int maxn=100005;
int n,m;
int t;
double a,b;
int N;
int n1;
int tre[maxn];
struct node
{
	int x,y,z;
	int sum;
	int id;
	int num;
	vector<int> ids; 
}dian[maxn],p1[maxn];
int fin[maxn];
bool comp1(node a,node b)
{
	if(a.x!=b.x) return a.x<b.x;
	else
	{
		if(a.y!=b.y) return a.y<b.y;
		else return a.z<b.z;
	}
}
bool comp2(node a,node b)
{
	return a.y<b.y;
}
int lowbit(int x)
{
	return x&(-x);
}
void add(int pos,int val)
{
	while(pos<=N)
	{
		tre[pos]+=val;
		pos+=lowbit(pos);
	}
}
int ask(int x)
{
	int ans=0;
	while(x)
	{
		ans+=tre[x];
		x=x-lowbit(x);
	}
	return ans;
}
void cdq(int l,int r)
{
	if(l==r) return;
	int mid=(l+r)>>1;
	cdq(l,mid),cdq(mid+1,r);//cdq分治,一层一层的分下去,类似于线段树一样。 
	sort(p1+l,p1+mid+1,comp2);
	sort(p1+mid+1,p1+r+1,comp2);//对左边和右边的y排序。 
	int j=l;
	for(int i=mid+1;i<=r;i++)
	{
		for(;j<=mid&&p1[j].y<=p1[i].y;j++)
		{
		//	if(dian[j].y<=dian[i].y)
		    add(p1[j].z,p1[j].num);//满足左边比右边的y小时就把z加入到树状数组。
			//只要一次j不满足就直接break掉,因为后面的j都比这个j大,也不可能满足小于关系。 
		//	else break;
		}
		p1[i].sum+=ask(p1[i].z);
		
	//	cout<<dian[i].id<<"--"<<dian[i].sum<<endl; 
		
	}
	for(j=j-1;j>=l;j--)
	{
		add(p1[j].z,-p1[j].num);
	}
	//将j前面加过的树状数组的值还原。 
}
int main()
{
	sd(t);
	while(t--)
	{
		memset(tre,0,sizeof(tre));
		sd(n);
		N=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d%d",&dian[i].x,&dian[i].y,&dian[i].z);
			dian[i].sum=0;
			dian[i].id=i;
			dian[i].num=1;
			N=max(N,dian[i].z);//树状数组的最大值应该是所有点中z最大的那个值,不优化的话就直接写maxn-1也行,优化后发现也就快了几十ms。。 
		}
		sort(dian+1,dian+1+n,comp1);
		
		int i=1;
		n1=0;
		while(i<=n)
		{
			p1[++n1]=dian[i];
			p1[n1].ids.pb(dian[i].id);
			int j=i+1;
			while(j<=n)
			{
				if(dian[j].x==dian[i].x&&dian[j].y==dian[i].y&&dian[j].z==dian[i].z)
				{
					p1[n1].ids.pb(dian[j].id);
					j++;
					p1[n1].num++;
				}
				else break;
			}
			i=j;
		}
		
		
		
		cdq(1,n1);
	/*	for(int i=1;i<=n;)
		{
			int j=i+1;
			int mx=dian[i].sum;
			for(;j<=n&&dian[i].x==dian[j].x&&dian[i].y==dian[j].y&&dian[i].z==dian[j].z;j++)
			{
				mx=max(mx,dian[j].sum);
			}
			for(int k=i;k<j;k++)
			{
				fin[dian[k].id]=mx;
			}
			i=j;
		}*/
		for(int i=1;i<=n1;i++)
		{
			int len=p1[i].ids.size();
			for(int j=0;j<len;j++)
			{
				int zz=p1[i].ids[j];
				fin[zz]=p1[i].sum+p1[i].num-1;
			}
		}
		for(int i=1;i<=n;i++)
		{
			printf("%d\n",fin[i]);
		}
	 } 
	 
	return 0;
} 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值