1239:统计数字

1239:统计数字

时间限制: 1000 ms         内存限制: 65536 KB
提交数: 3788     通过数: 1640 
【题目描述】
某次科研调查时得到了nn个自然数,每个数均不超过1500000000(1.5×10915000000001.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≤100080%的数据满足:1≤n≤500001≤n≤50000100%的数据满足:1≤n≤2000001≤n≤200000,每个数均不超过1500000000(1.5×10915000000001.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;
}
原文地址:https://www.cnblogs.com/wendcn/p/12577052.html