原题连接:http://acm.hdu.edu.cn/showproblem.php?pid=4325
题意:给n朵花,每朵花都有一个开花时间(是一个区间),给q次查询命令,每次查询输入一个时间点,询问该时刻共有多少花正在开……
思路:这就是一个树状数组的 插线问点 算法,但是 注意 范围1 <= Si <= Ti <= 10^9 数组是开不下的,而且n,q 的范围比较小,这就说明要先对数据进行离散化处理,缩小范围即可。。但是怎么离散化呢?首先我们 看题,查询是离线的,我们可以将开花的时间区间和查询一次全部离散化了,这样就不影响最终结果了。
首先我们在输入每组数据是要记录编号,记录在flo 中(即输入顺序) -----参考代码 P1 处
然后我们根据num 值的大小对结构体排序
接着我们通过访问编号将num值离散化,注意可能有重复的num,保存在 AC[i]=j数组中,i是输入时的编号,j是离散后的数值。 ------P2
最后通过编号在树状数组ans中插线,然后问点。-----P3
AC代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int Max=110000;
int AC[Max],ans[Max];
struct hello
{
int num;
int id;
}flo[Max];
int lowbit(int i)
{
return i&(-i);
}
void update(int x,int y)
{
while(x<Max)
{
ans[x]+=y;
x+=lowbit(x);
}
}
int find(int x)
{
int sum=0;
while(x>0)
{
sum+=ans[x];
x-=lowbit(x);
}
return sum;
}
bool cmp(hello t1,hello t2)
{
return t1.num<t2.num;
}
int main()
{
int i,j,n,m,ncase,q;
scanf("%d",&ncase);
for(m=1;m<=ncase;m++)
{
int s,t,cnt=0;
printf("Case #%d:\n",m);
memset(ans,0,sizeof(ans));
memset(AC,0,sizeof(AC));
scanf("%d%d",&n,&q);
for(i=0;i<2*n+q;i++)
{
scanf("%d",&flo[i].num); //------P1
flo[i].id=i;
}
sort(flo,flo+2*n+q,cmp);
for(i=0;i<2*n+q;i++)
{
if(i==0||flo[i].num!=flo[i-1].num) //--------P2
AC[flo[i].id]=++cnt;
else
AC[flo[i].id]=cnt;
}
for(i=0;i<2*n;i+=2) //--------P3
{
update(AC[i],1);
update(AC[i+1]+1,-1);
}
for(i=2*n;i<2*n+q;i++)
printf("%d\n",find(AC[i]));
}
}