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\n",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;
}

 

posted @ 2013-11-28 14:14  辰曦~文若  阅读(558)  评论(0编辑  收藏  举报