题目:http://acm.hdu.edu.cn/showproblem.php?pid=5297
题意:给定整数n和整数r,在1、2、3、4、5.......的序列中删掉能够开2次方的数,3次方的数,4次方的数,.......r次方的数,剩下的数称为Y序列,求Y序列中第n个数是多少。
分析:对于一个数x,假设求出x在Y序列的位置就好办了。
先无论序列中的1,假如r=3,能够开2次方的数有4,9,16,25,36,49,64.....
能够开3次方的数有8,27,64.......
我们把能够开2次方的数的个数减掉,再把能够开3次方的数的个数减掉,会发现减多了(把既能够开2次方又能够开3次方的数减了两次),然如把减多了的补回来,就要把能够开6次方的数的个数加回来,这样就能够想到容斥原理:先将全部的2~r的数里面的素数组合起来(那些有1个以上某个同样的因子的数不用考虑,之前肯定会被减掉),然后枚举这些数,看这些数的质因子为奇数和还是偶数个........
得到x的位置后,開始想到用二分,没想到超时了,,,,,,看了一下别人的思路,用的是迭代...改成迭代就a了。
代码:
#include <iostream> #include <cstdio> #include <cmath> using namespace std; int prime[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67}; int cnt[100]={19,0,1,1,1,1,2,1,1,1,2,1,2,1,2,2,1,1,2,1,2,2,2,1,2,1,2,1,2,1,3,1,1,2,2,2,2,1,2,2 ,2,1,3,1,2,2,2,1,2,1,2,2,2,1,2,2,2,2,2,1,3,1,2,2,1}; long long considrt[6000000],nconsidrt; void GetConsider(int r) { nconsidrt=0; int i,j,sz; for(i=0;prime[i]<=r;i++) { sz=nconsidrt; for(j=0;j<sz;j++) { if(considrt[j]*prime[i]<=63) considrt[nconsidrt++]=considrt[j]*prime[i]; } considrt[nconsidrt++]=prime[i]; } } long long cal(long long x) { long long ret(0); for(int i=0;i<nconsidrt;i++) { if(cnt[considrt[i]]&1) ret+=(long long)pow(x+0.5,1.0/considrt[i])-1; else ret-=(long long)pow(x+0.5,1.0/considrt[i])-1; } return x-ret-1; } long long solve(long long n,int r) { GetConsider(r); long long ans = n; while(1) { long long temp = cal(ans); if(temp == n) break; ans += n - temp; } return ans; } int main() { int ncase,r; long long n; scanf("%d",&ncase); while(ncase--) { scanf("%I64d%d",&n,&r); printf("%I64d ",solve(n,r)); } return 0; }