POJ2773 HAPPY 2006(不能对LL大方之战)

题目大意:就是求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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值