题目描述:
有n个整数(1<=n<=200000),分别为a1, a2, a3, ..., an。对于第i个整数ai,它的范围为0~10^9, 有k个整数(1<=k<=200000),分别为M1, M2, M3, ..., Mk。对于第i个整数Mi,它的范围为0~10^9。你的任务是对M1, M2, M3, ..., Mk这k个整数分别查找它们在a1, a2, a3, ...., an这个序列里出现的次数。
输入描述:
有多组数据。每组数据3行。结束标志为EOF。
第一行输入为两个整数n, k(1<=n, k<=200000)。N为整数的个数,k为数据的组数。
第二行输入为n个整数,分别为a1, a2, a3, ..., an。对于第i个整数ai,它的范围为0~10^9。整数之间间隔为一个空格。
第三行为k个整数。分别为M1, M2, M3, ..., Mk。对于第i个整数Mi,它的范围为0~10^9。整数之间间隔为一个空格。
每一个数据文件有多组数据,但保证每一个数据文件里的每组数据的n的和不超过200000,每一个数据文件里的每组数据的k的和不超过200000。
比如下面的样例输入,出现了两个n和k,每个n和k的范围都在1~200000以内且两个n的和不超过200000,两个k的和也不超过200000。
输出描述:
对于每组数据输出一行。对于每个Mi,输出在a1, a2, a3, ..., an这个序列里出现的次数。数与数之间需要间隔一个空格。注意这行最后不能出现空格。
输入样例:
5 5
1 2 3 4 5
3 5 2 4 1
10 10
656 649 1646 1322 123 649 3201 9850 1245 3200
666 110 1000 649 9850 121 123 9850 1244 1646
输出样例:
1 1 1 1 1
0 0 0 2 1 0 1 0 1
问题分析:
这道题目,我第一个想到的就是二分查找,因为数据量有点大,我们只需要先将数组排序,然后二分查找第一个出现该数字的位置,再从该位置向前向后遍历就好。但是,这样子,复杂度依然很大,于是,,我想到一种新的方法,其实,也很简单,就是在找到第一个出现该数字的位置时,继续二分查找该数字出现的上界和下界,相减加一就是该数字在数组中出现的次数。很好,其实本题并不难,主要是为了优化时间,要二分查找上界和下界。下面上代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
const int Maxn = 200000;
using namespace std;
int Find(int a[],int low,int high,int t)
{
int m;
while (low <= high)
{
m = low + (high - low) / 2;
if(a[m] < t)
{
low = m + 1;
}
else if(a[m] > t)
{
high = m - 1;
}
else
{
return m;
}
}
if(a[low] == t)
{
return low;
}
return -1;
}
int main(void)
{
int i,n,m,t,p,sum,j,a[Maxn],m1,m2,m3;//m1,m2用来保存上界和下界
while (~scanf ("%d%d",&n,&t))
{
for (i = 0; i < n; i++)
{
scanf ("%d",&a[i]);
}
sort(a,a + n);
for (i = 1; i <= t; i++)
{
sum = 0;
scanf ("%d",&p);
m = Find(a,0,n - 1,p);
//printf ("%d %d\n",p,m);
if(m == -1)
{
sum = 0;
}
else
{
m3 = m;
if(m != n - 1)
{
//查找数字出现的上界,用m1保存
while (a[m + 1] == a[m])
{
m = Find(a,m + 1,n - 1,p);
}
}
m1 = m;
if(m3 != 0)
{
//查找数字出现的下界,用m2保存
while (a[m3 - 1] == a[m3])
{
m3 = Find(a,0,m3 - 1,p);
}
}
m2 = m3;
//上界减去下界+1等于该数字在数组上出现的次数
sum += (m1 - m2 + 1);
}
printf ("%d",sum);
if(i < t)
{
putchar(' ');
}
}
putchar('\n');
}
return 0;
}