参考链接
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; }