poj2352

12 篇文章 0 订阅
6 篇文章 0 订阅

题目有难度,离散化+线段树。

大致题意为:给定n个点的坐标(x,y),定义层次level(i):若对于某个点p,在所有输入的点中,若Xq<=Xp && Yq<=Yp,所有满足该条件的其它点的个数(不包括p点本身)为i,则记作:p点属于level(i),现在就是要求出所有的level(i)个数,即level(0)、level(1),……,level(n-1)各包含点个数。

注意题目中点输入的顺序为按y坐标从小到大,在y坐标相同时,再按x坐标从小到大,这对于解题提供了方便。

仔细分析题目,大致的思路就是判断每个点属于第几层次,然后将该层次个数增一,不断循环直至全部点都判断完,思路很简单,难点在于如何判断输入那个层次。朴素的算法就是枚举所有的点判断满足该条件的点的个数,累加就是层次。当这样的时间复杂度为0(n*n),铁定TLE。有没有更简单的算法呢?这里其实可以简化,由于题目中点的输入顺序,故后输入的点一定不属于满足前输入点判定条件的队列中。所以只需要枚举前输入点即可。其次:对于前输入点由于输入的关系,其实也只用判断x坐标即可,即前输入点中所有x坐标小于等于判定点x坐标的点个数即为该待判定点的所属层次。

分析到这里提供两种解题方案:

一)递推: 有上述分析可知:对于待判定的点,其层次为:所有前输入点中x坐标<=判定点x坐标个数。这样的话就可以写出朴素的算法,但仍然TLE。

二)线段树+离散化:同样由上述分析可以推知:对于待输入的点,其层次为:0<=所有前输入点中x坐标<=判定点x坐标  点个数(与y坐标无关),这样的话我们可以首先将所有点的x坐标离散化(从1开始)。然后对于待判定的点,求出线段树中x坐标在1——x(待输入点x坐标)之间(包括1和x)的点个数即可,之后累加相应的层次数,然后插入该x坐标,继续下一次判定,不断循环,直至所有点的层次都求出来,最后就是输出结果了。

本题线段树的难点在于如何构造线段树节点权值,其定义是什么,以及如何查询线段树。

线段树节点权值定义:前输入点中x坐标在线段树节点两端点范围内的点个数。

查询线段树策略:查询前输入点中x坐标在1——待查询点x坐标范围内的点个数(包括1和x)

首先给出第一种方法代码:仅供参考

下面是代码:(TLE)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Max 16000
int dp[Max];
int num[Max][Max/100];
int number[Max];
int point[Max][Max/100];
int n;
int main(){
	scanf("%d",&n);
	memset(dp,0,sizeof(dp));
	bool trag=true;
	int pivot=1,index=1,i,j,k,a,b;
	scanf("%d%d",&a,&b);
	point[1][1]=a;
	num[1][1]=0;
    dp[0]++;
	int yy=b;
    for(i=1;i<n;i++){
		scanf("%d%d",&a,&b);
	    if(b==yy && trag){
			point[pivot][++index]=a;
			num[pivot][index]=num[pivot][index-1]+1;
			dp[num[pivot][index]]++;
		}
		else if(b>yy){
			yy=b;
			number[pivot]=index;
			pivot++;
			index=1;
			point[pivot][index]=a;
			num[pivot][index]=0;
			int Sum=0;
			for(j=1;j<pivot;j++)
				for(k=number[j];k>=1;k--)
					if(point[j][k]<=a){
						Sum+=(num[j][k]+1);
						break;
					}
			dp[Sum]++;
			trag=false;
		}
		else if(!trag && b==yy){
			point[pivot][++index]=a;
			num[pivot][index]=num[pivot][index-1]+1;
			int Sum=num[pivot][index];
			for(j=1;j<pivot;j++)
				for(k=number[j];k>=1;k--)
					if(point[j][k]<=a){
						Sum+=(num[j][k]+1);
						break;
					}
			dp[Sum]++;
		}
	}
	for(i=0;i<n;i++)
		printf("%d\n",dp[i]);
    return 0;
}

线段树+离散化

下面是代码:780K+188MS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define Max 15010
#define Lson(p) (p<<1)
#define Rson(p) (p<<1|1)
#define Mid(a,b) ((a+b)>>1)
using namespace std;
struct Point{ //离散化辅助数组
	int truth; //真实x值
	int index; //对应输入下标
}point[Max]; 
struct Node{ // 线段树节点
	int l,r,value;
}node[3*Max];
int Input[Max]; // 输入x坐标
int ans[Max]; //结果
int n;
bool cmp(const struct Point p,const struct Point q){ //覆盖sort的cmp
	return p.truth<q.truth;
}
void build_tree(int left,int right,int po){ //建树
	node[po].l=left,node[po].r=right,node[po].value=0;
	if(left==right)
		return ;
	int mid=Mid(left,right);
	build_tree(left,mid,Lson(po));
	build_tree(mid+1,right,Rson(po));
}
void update_tree(int v,int po){ //更新线段树
	node[po].value++; //若子线段含有该点,则母线段必有该点,故在寻找到根节点的路径上的所有节点的权值均要加一
	if(node[po].l==node[po].r) //找到根节点
		return ;
	int mid=Mid(node[po].l,node[po].r); 
	if(v<=mid) //左子树中
		update_tree(v,Lson(po));
	else //右子树中
		update_tree(v,Rson(po));
}
int search_tree(int left,int right,int po){ //查询选段数,查询在left——right之间的点个数
	if(left==node[po].l && right==node[po].r) //若找到,则返回个数
		return node[po].value;
	int mid=Mid(node[po].l,node[po].r);
	if(right<=mid) //在左子树中继续查找
		return search_tree(left,right,Lson(po));
	else if(left>mid) //在右子树中继续查找
		return search_tree(left,right,Rson(po)); 
	else //left——right个数=left——mid个数+mid+1——right个数
		return search_tree(left,mid,Lson(po))+search_tree(mid+1,right,Rson(po));
}	
int main(){
	scanf("%d",&n);
	int temp;
	for(int i=0;i<n;i++){ //离散化准备
		scanf("%d%d",&Input[i],&temp);
		point[i].truth=Input[i]; //真值x
		point[i].index=i;//输入下标
	}
	sort(point,point+n,cmp); //排序
	temp=point[0].truth;
	int Count=1;
	for(int i=0;i<n;i++){ //离散化核心代码
		if(point[i].truth>temp){
			Count++;
			temp=point[i].truth;
		}
		Input[point[i].index]=Count;
	}
	build_tree(1,Count,1); //建树
	memset(ans,0,sizeof(ans)); // 初始化个数均为0
	for(int i=0;i<n;i++){ // 更新线段数+查找线段树
		ans[search_tree(1,Input[i],1)]++; //先查找线段树,由于是判断前输入节点
		update_tree(Input[i],1); // 插入该节点x坐标,更新线段树
	}
	for(int i=0;i<n;i++) // 输出结果
		printf("%d\n",ans[i]);
	return 0;
}
		

下面是树状数组代码:452K+172MS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define Max 15010
#define lowbit(a) (a&(-a))
using namespace std;
struct Node{
	int index;
	int truth;
}node[Max];
int level[Max];
int Input[Max];
int stree[Max];
int N,n;
bool cmp(const Node &p,const Node &q){
	return p.truth<q.truth;
}
void update_tree(int index){
	while(index<=n){
		stree[index]++;
		index+=lowbit(index);
	}
}
int add_tree(int index){
	int Sum=0;
	while(index>0){
		Sum+=stree[index];
		index-=lowbit(index);
	}
	return Sum;
}
int main(){
	scanf("%d",&N);
	int temp;
	for(int i=0;i<N;i++){
		scanf("%d%d",&Input[i],&temp);
		node[i].index=i;
		node[i].truth=Input[i];
	}
	sort(node,node+N,cmp);
	int Count=1;
	temp=node[0].truth;
	for(int i=0;i<N;i++){
		if(node[i].truth>temp){
			Count++;
			temp=node[i].truth;
		}
		Input[node[i].index]=Count;
	}
	n=Count;
    memset(stree,0,sizeof(stree));
	memset(level,0,sizeof(level));
	for(int i=0;i<N;i++){
		level[add_tree(Input[i])]++;
		update_tree(Input[i]);
	}
	for(int i=0;i<N;i++)
		printf("%d\n",level[i]);
	return 0;
}		  	



 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值