NYOJ-600 花儿朵朵【离散化+树状数组】

花儿朵朵

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 5
描述
春天到了,花儿朵朵盛开,hrdv是一座大花园的主人,在他的花园里种着许多种鲜花,每当这个时候,就会有一大群游客来他的花园欣赏漂亮的花朵,游客们总是会询问,某个时间有多少种花儿同时在盛开着?hrdv虽然知道每种花儿的开花时间段,但是他不能很快的答出游客的问题,你能编写一个程序帮助他吗?
输入
第一行有个整数t,表示有t组测试数据,每组测试数据第一行为两个整数n,m(0<n<100000,0<m<100000);随后有n行,每一行有两个整数x,y(0<x<y<1000000000),表示这一种花的盛开时间是从x到y;随后有m行,每行有一个整数,代表游客询问的时间。
输出
对于每次游客的询问,输出一个整数在单独的一行,表示这个时间盛开的花有多少种。
样例输入
2
1 1
5 10
4
2 3
1 4
4 8
1
4
6
样例输出
0
1
2
1

这个一读题就感觉可以用线段树做,然后想了想用树状数组更好,就愉快的决定用取线问点了。但是问题就来了,题中给的数据是从1到10亿这么大的数据范围明显不能开出来。看了看别人的的讲解,说要离散化。鄙人就把自己对离散化的理解发出来,板砖轻拍。

先来看看百度里离散化的概念:离散化,把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。其基本思想就是在众多可能的情况中“只考虑我需要用的值”。

光看到概念鄙人还是云里雾里的。后来写了几个数据推一推就想明白了:

 6 4

100 1000

200 400

150 250

500 750

700 900

800 900

50

450

900

1200

对全部数据进行顺序存储然后在由小到大排序,建立如下的对应关系:


这样就相当于对数据进行了一个压缩,建立了一中映射。只考虑了我需要用的的数据,即 花儿开放的区间段(相同颜色的线),和顾客问的时间点(灰色的线)

像绿色的线:100-1000段的数据就映射成了2-13。段期间的值都是有需要的值。

把压缩前的数据用线段图表示:黑线段表示开花期间,红线表示这一时间点


把压缩后的数据用线段图表示:


这样只需要14个点就能存储,而压缩前要用1200个点。然后就是建立线段树或树状数组了,这些不懂的童鞋先做做士兵杀敌系列吧。

代码列出:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define Maxsize 300000
int a[Maxsize];
int b[Maxsize];
struct Node{
	int id;
	int data;
}d[Maxsize];
int num,n,m;
int com(const void *p,const void *q)
{
	Node *r;
	Node *t;
	r=(Node *)p;t=(Node *)q;
	return r->data-t->data;
}
int lowbit(int x)
{
    return x&(-x);
}
int getsum(int x)//上溯寻值
{
	int s=0;
    while(x<=Maxsize)
     {
         s+=b[x];
         x+=lowbit(x);
     }
	return s;
}
void update(int x,int y)
{
    while(x>0)
     {
         b[x]+=y;
         x-=lowbit(x);
     }
}
int main()
{
	int t;
	int i;
	int count;
	scanf("%d",&t);
	while(t--)
	{
		memset(b,0,sizeof(b));
		d[0].data=-1;
		d[0].id=-1;
		scanf("%d%d",&n,&m);
		num=n*2+m;
		for(i=1;i<=num;i++)
		{scanf("%d",&d[i].data);d[i].id=i;}
		qsort(d,num+1,sizeof(Node),com);
		count=0;
		a[d[1].id]=++count;
		for(i=1;i<=num;i++)
		{
			if(d[i].data!=d[i-1].data)
				a[d[i].id]=++count;
			else a[d[i].id]=count;
		}
		num=num-m;
		for(i=2;i<=num;i+=2)
		{
			update(a[i],1);
			update(a[i-1]-1,-1);
		}
		for(i=num+1;i<=num+m;i++)
		{
			printf("%d\n",getsum(a[i]));
		}
	}
	return 0;
}        



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值