题目大意:就是求m的第k个(升序)与m互质的数。
思路:看了discuss才知道欧拉函数 的一个性质可以解决这道题:就是如果在[1,m]之间有cnt个数和m互质,即欧拉函数为cnt,那么在[n*m+1,(n+1)*m](n为正整数)区间内的欧拉函数也为cnt,即是有循环节。这就好办了,循环节最大才为100万,那么找到k/cnt这个区间,就可以枚举求得第K个数了。不过我写的实在很猥琐,1670ms撸过~~囧。。但是后来把LL改为int,1290ms撸过~~~囧。。以后不能大方了~~囧。。
AC program:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
typedef __int64 LL;
LL pp[100000],kg;
LL nn[100000];
LL ff[1000005];
void get_break(LL m)
{
memset(pp,0,sizeof(pp));
memset(ff,0,sizeof(ff));///
LL tmp=m;
kg=0;
for(LL i=2;i*i<=tmp;)
{
if(m%i==0)
{
pp[kg]=i;
LL cnt=0;
while(m%i==0)
{
m/=i;
cnt++;
}
nn[kg++]=cnt;
}
else i++;
}
if(m!=1)
{
pp[kg]=m;
nn[kg++]=1;
}
}
LL gcd(LL a, LL b)
{
return b?gcd(b,a%b):a;
}
int main()
{
LL m,k;
while(cin>>m>>k)
{
if(m==1&&k==1){cout<<1<<endl;continue;}
get_break(m);
LL sum=1;
for(LL i=0;i<kg;i++)
{
sum= sum*(pp[i]-1)*(int)pow(pp[i]*1.0,(nn[i]-1)*1.0);
}
// cout<<"sum "<<sum<<endl;
//n*m+1---(n+1)*m
LL cur;
LL newk=k%sum;
if(newk==0){ newk=sum; cur=(k-1)/sum;}
else cur=k/sum;
//取模为0的时候就是刚好在循环节上;
//cout<<"newk "<<newk<<endl;
LL cnt=0;
for(LL i=cur*m+1;i<=(cur+1)*m;i++)
{
if(cur>=1)
{
if(gcd(i,m)==1)
cnt++;
if(cnt==newk)//这次不是K了,法克
{cout<<i<<endl; break;}
}
if(cur==0)
{
if(gcd(m,i)==1) //m>i
cnt++;
if(cnt==newk) //注意改全
{cout<<i<<endl; break;}
}
}
}
return 0;}
还有经典的二分加上容斥定理 program:(0ms秦松撸过的啊~~)
#include<iostream>
#include<stdio.h>
#define N 1005
using namespace std;
int n , k, K, cnt;
int mark[N + 10], prime[N];
int a[11];
void init()
{
cnt = 0;
for(int i = 2; i <= N; i++)
{
if(!mark[i])
{
prime[cnt++] = i;
for(int j = 2 * i; j <= N; j += i)
{
mark[j] = 1;
}
}
}
}
void getprime()
{
int tmp = n;
k = 0;
for(int i = 0; i < cnt && prime[i] * prime[i] <= n; i++)
{
if(tmp % prime[i] == 0)
{
a[k++] = prime[i];
while(tmp % prime[i] == 0)
{
tmp /= prime[i];
}
}
}
if(tmp != 1)
{
a[k++] = tmp;
}
}
int judge(int num)
{
int sum = 0;
for(int i = 1; i < (1 << k); i++)
{
int tmp = 1;
int t = 0;
for(int j = 0; j < k; j++)
{
if((1 << j) & i)
{
t++;
tmp = tmp * a[j];
}
}
if(t % 2 == 1)
{
sum += num / tmp;
}
else
{
sum -= num / tmp;
}
}
return num - sum;
}
int main()
{
init();
while(scanf("%d%d", &n, &K) != -1)
{
getprime();
int l = 1, r = 1000000000, mid, ans;
while(l <= r)
{
mid = (l + r) >> 1;
int pt = judge(mid);
if(pt >= K)
{
if(pt == K) ans = mid;
r = mid - 1;
}
else
{
l = mid + 1;
}
}
printf("%d\n", ans);
}
}
二分的算法转载来自:http://blog.163.com/shrimp_wy/blog/static/1912435302011972611105/
代码来自:http://hi.baidu.com/qiusijian/item/75234371533f7346ef1e534a