zoukankan      html  css  js  c++  java
  • UVA 11610 Reverse Prime (数论+树状数组+二分,难题)

    参考链接
    http://blog.csdn.net/acm_cxlove/article/details/8264290
    http://blog.csdn.net/w00w12l/article/details/8212782

    题意:
      首先定义了一种叫做Reverse Prime的数:是一个7位数,倒置后是一个<=10^6的素数(如1000070)
      然后要把所有的Reverse Prime求出来,排好序。
      然后题目有2种操作:
      q x :求编号0到编号x的Reverse Prime的质因数个数的和
      d x :从表中删掉x(x是一个Reverse Prime)

    思路:
      1.先按照题目要求筛选素数,同时求出每个数的只因数个数,再将小于10^6的素数倒置,转化成Reverse Prime,排序离散化
      2.建立两个树状数组,cnt存储区间内的个数,num存储区间内的质因数个数和。
      3.当执行q x 操作时,二分查找最小的mid值,使得sumcnt(mid)=++x(因为x是从0开始的,所以要+1),然后对num树状数组的1~mid区间求和
      4.当执行d x 操作时,可以用二分查找或者map映射获取x的下标,然后对两个树状数组进行更新即可。

      最后要注意的是,由于Reverse Prime是由10^6以下的素数倒置得到的,那么得到的要求是7位,最后一位一定是0,
      我们可以对每个除以10处理。即不考虑末尾的0,最后求质因数个数的时候记得加上2(最后末尾0的因子2和5)。

    #include <iostream>
    #include <stdio.h>
    #include <algorithm>
    #include <string.h>
    #include <map>
    
    using namespace std;
    const int maxn=1000001;
    int isprime[maxn];  //标记素数
    int factor[maxn];  //factor[i]存储i的质因数个数
    int prime[maxn];  //存储素数
    int idx;  //prime数组的下标
    long long cnt[maxn];  //统计区间内reverse prime number的个数
    long long num[maxn]; //统计区间内reverse prime number的质因数的个数
    map<int,int> m;
    
    struct RPrimeNum{
        int num;
        int factor;  //存储质因数的个数
        bool operator<(const RPrimeNum tmp)const{
            return num<tmp.num;
        }
    }reverse_prime_num[maxn];
    int ridx;  //reverse_prime_num数组的下标
    
    int lowbit(int x){
        return x&(-x);
    }
    //对数组cnt的更新操作
    void updatecnt(int i,int v){
        while(i<=ridx){
            cnt[i]+=v;
            i+=lowbit(i);
        }
    }
    //对数组cnt的求和操作
    long long sumcnt(int i){
        long long res=0;
        while(i){
            res+=cnt[i];
            i-=lowbit(i);
        }
        return res;
    }
    //对数组num的更新操作
    void updatenum(int i,int v){
        while(i<=ridx){
            num[i]+=v;
            i+=lowbit(i);
        }
    }
    //对数组num的求和操作
    long long sumnum(int i){
        long long res=0;
        while(i){
            res+=num[i];
            i-=lowbit(i);
        }
        return res;
    }
    
    void init(){
        memset(isprime,0,sizeof(isprime));
        memset(factor,0,sizeof(factor));
        idx=-1;
        //利用素数筛选法求质因数的个数
        //因为所有素数都是6位数,但是题目要求是7位数,可见原数的最低位都为0,可以先不考虑这个0,因此maxn的值为1000001
        for(int i=2;i<maxn;i++){
            if(!isprime[i]){
                prime[++idx]=i;
                for(int j=i*2;j<maxn;j+=i){
                    isprime[j]=i;   //记录它被哪个数所筛
                }
            }
        }
        //求一个数的质因数个数
        for(int i=2;i<maxn;i++){
            if(!isprime[i])
                factor[i]=1;
            else
                factor[i]=factor[i/isprime[i]]+1;   //1 即为prime[i]
        }
        ridx=0;
        int n,rnum;
        for(int i=0;i<=idx;i++){
            n=prime[i];
            rnum=0;
            while(n){
                rnum=rnum*10+n%10;
                n=n/10;
            }
            while(rnum<100000){
                rnum*=10;
            }
            reverse_prime_num[++ridx].num=rnum*10;
            reverse_prime_num[ridx].factor=factor[rnum]+2;  //2:最后一位没考虑的0,即2和5两个质因子
        }
        sort(reverse_prime_num+1,reverse_prime_num+ridx+1);  //额,前面第一个应该+1的,一不小心给漏了
    
        for(int i=1;i<=ridx;i++)
            m[reverse_prime_num[i].num/10]=i;  //map映射
    
        memset(num,0,sizeof(num));
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=ridx;i++){
            updatenum(i,reverse_prime_num[i].factor);
            cnt[i]=lowbit(i); //因为更新的值为1,所以只要直接赋值lowbit(i)即可
        }
    }
    //二分搜索第m个reverse prime对应的下标
    int binarySearch1(int m){
        int l=1,r=ridx,mid;
        long long ans;
        while(r>=l){
            mid=(l+r)>>1;
            ans=sumcnt(mid);
            if(ans==m)
                return mid;
            if(m<ans)
                r=mid-1;
            else
                l=mid+1;
        }
    }
    //也可以通过二分搜索reverse prime对应的下标
    int binarySearch2(int m){
        int l=1,r=ridx,mid;
        while(r>=l){
            mid=(l+r)>>1;
            if(m==reverse_prime_num[mid].num)
                return mid;
            if(m<reverse_prime_num[mid].num)
                r=mid-1;
            else
                l=mid+1;
        }
    }
    int main()
    {
        char str[3];
        int v;
        init();
        while(scanf("%s%d",str,&v)!=EOF){
            if(str[0]=='q'){
                v++;
                int u=binarySearch1(v);
                printf("%lld
    ",sumnum(u));
            }
            else{
                //int u=binarySearch2(v);  //二分超找对应的下标
                int u=m[v/10];  //通过建立map映射获取下标
                updatecnt(u,-1);
                updatenum(u,-reverse_prime_num[u].factor);
            }
        }
        return 0;
    }
  • 相关阅读:
    (OK) error: code model kernel does not support PIC mode
    compile android for x86_64
    内核开发的前途在什么地方,发展方向有哪些?
    SOA 与 MSA(微服务架构)
    [Android] adb命令如何获取android手机屏幕分辨率
    (2) 在 Build 系统中添加新的内容
    (1) 理解 Android Build 系统
    (OK) [solved] error
    Error while building CM 13 (KERNEL_OBJ/usr, needed by libtinyalsa_intermediates/mixer.o)
    (OK) http://www.android-x86.org
  • 原文地址:https://www.cnblogs.com/chenxiwenruo/p/3447615.html
Copyright © 2011-2022 走看看