1239:统计数字 时间限制: 1000 ms 内存限制: 65536 KB 提交数: 3788 通过数: 1640 【题目描述】 某次科研调查时得到了nn个自然数,每个数均不超过1500000000(1.5×109)1500000000(1.5×109)。已知不相同的数不超过1000010000个,现在需要统计这些自然数各自出现的次数,并按照自然数从小到大的顺序输出统计结果。 【输入】 第一行是整数nn,表示自然数的个数; 第2 n+12 n+1每行一个自然数。 【输出】 包含mm行(mm为nn个自然数中不相同数的个数),按照自然数从小到大的顺序输出。每行输出两个整数,分别是自然数和该数出现的次数,其间用一个空格隔开。 【输入样例】 8 2 4 2 4 5 100 2 100 【输出样例】 2 3 4 2 5 1 100 2 【提示】 数据范围: 40%的数据满足:1≤n≤10001≤n≤1000; 80%的数据满足:1≤n≤500001≤n≤50000; 100%的数据满足:1≤n≤2000001≤n≤200000,每个数均不超过1500000000(1.5×109)1500000000(1.5×109)。 本题目禁止使用STL及包含可以使用的相关调用。
【分析】
一、从数据规模判断
1:每个数不超过1.5X10^9,意思是不让我们用桶排序。(当然这个数据小一点,那这题就简单了)
2:对于100%的数据:n<=200000,不相同数字不超过10000个。这个数据告诉我们,程序不能做到平方级,连O(nm)都会爆掉,最多O(nlgn),能做到O(n)当然更棒。其实这也在提醒我们二分法的存在。何处用二分法?桶排序简单就在于查找是O(1),没有桶排序我们要自己查找,一般是O(m),但不能用,那就二分了。
3:二分法查找意味着要排序,结合本题,我们考虑插入排序。先算一下,插入排序需要元素后移,时间复杂度估计:最坏可能数据序序进入,每次全移,1+2+3+....+m=m(m-1)/2,还可以接受吧。二分查找时间复杂度本题为O(nlgm),这个应该可以接受。
二、二分查找有点小心思
为了造就二分查找开区间问题(让边界不符合条件,要不然会多两次判断),加入两个虚拟边界,左边界a[0]=-1,右边界2e9,输入的每一个数都介于这两数之间,不会取到边界。同时为了表达方便,把数据序号扩大一倍,全存在偶数位上。(详见代码)
三、AC代码
//1239:统计数字 #include<iostream> using namespace std; int const inf=2e9; int n,a[20001],b[20001],x,cnt,p; int pos(int x,int l,int r) { int m=(l+r)/2; if(r-l==2)return m; if(m%2)m--; if(x==a[m])return m; if(x<a[m])return pos(x,l,m); return pos(x,m,r); } int main(){ ios::sync_with_stdio(false); a[0]=-1,a[2]=inf; cin>>n; for(int i=1;i<=n;i++) { cin>>x; p=pos(x,0,cnt+2); if(p%2) { cnt+=2; for(int j=cnt+2;j>p+1;j-=2)a[j]=a[j-2],b[j]=b[j-2]; a[p+1]=x,b[p+1]=1; } else b[p]++; } for(int i=2;i<=cnt;i+=2)cout<<a[i]<<' '<<b[i]<<endl; return 0; }