bzoj4652 [NOI2016]循环之美

Description

牛牛是一个热爱算法设计的高中生。在他设计的算法中,常常会使用带小数的数进行计算。牛牛认为,如果在 k 
进制下,一个数的小数部分是纯循环的,那么它就是美的。现在,牛牛想知道:对于已知的十进制数 n 和 m,在 
kk 进制下,有多少个数值上互不相等的纯循环小数,可以用分数 xy 表示,其中 1≤x≤n,1≤y≤m,且 x,y是整数
。一个数是纯循环的,当且仅当其可以写成以下形式:a.c1˙c2c3…cp-1cp˙其中,a 是一个整数,p≥1;对于 1
≤i≤p,ci是 kk 进制下的一位数字。例如,在十进制下,0.45454545……=0.4˙5˙是纯循环的,它可以用 5/11
、10/22 等分数表示;在十进制下,0.1666666……=0.16˙则不是纯循环的,它可以用 1/6 等分数表示。需要特
别注意的是,我们认为一个整数是纯循环的,因为它的小数部分可以表示成 0 的循环或是 k?1 的循环;而一个小
数部分非 0 的有限小数不是纯循环的。

Input

只有一行,包含三个十进制数N,M,K意义如题所述,保证 1≤n≤10^9,1≤m≤10^9,2≤k≤2000
 

Output

一行一个整数,表示满足条件的美的数的个数。
 

Sample Input

2 6 10

Sample Output

4
explanation
满足条件的数分别是:
1/1=1.0000……
1/3=0.3333……
2/1=2.0000……
2/3=0.6666……
1/1 和 2/2 虽然都是纯循环小数,但因为它们相等,因此只计数一次;同样,1/3 和 2/6 也只计数一次。
 
 
 
杜教筛板子题
柿子还是比较好推
这里补一个我一直没搞懂的证明
 
求证:k进制下的纯循环小数的最简分数的分母一定与k互质
 
设循环节长度为t,最简分数为a/b
所以

 

 
// luogu-judger-enable-o2
//%std
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
#define lovelive long long
#define lc son[x][0]
#define rc son[x][1]
#define lowbit(x) (x&(-x))
#define pt vc
const lovelive mod=19260817;
const int N=2e6+100;
void read(lovelive &x)
{
  lovelive p=1;
  x=0;
  char c=getchar();
  while(c<'0'||c>'9')
  {
    if(c=='-')
      p=-1;
    c=getchar();
  }
  while(c>='0'&&c<='9')
  { 
      x=x*10+c-48;
      c=getchar();
  } 
  x*=p;
}
struct hash_map{
  int ans[1000010],nxt[1000010],fir[mod+1],tot;
  lovelive hash[1000010];
  void add(lovelive x,int y)
  {
    int i=x%mod;
    nxt[++tot]=fir[i];
    fir[i]=tot;
    ans[tot]=y;
    hash[tot]=x;
  }
  int query(lovelive x)
  {
    for(int j=fir[x%mod];j;j=nxt[j])
      if(hash[j]==x)
        return ans[j];
    return -1;
  }
}sp;
int prime[N],miu[N],pre[N],tot;
bool not_prime[N];
void find_prime(int n)
{
  miu[1]=1; 
  for(int i=2;i<=n;i++)
  {
    if(!not_prime[i])
      prime[++tot]=i,miu[i]=-1,pre[i]=i;
    for(int j=1;j<=tot&&prime[j]*i<=n;j++)
    {
      not_prime[i*prime[j]]=true;
      pre[i*prime[j]]=prime[j];
      if(i%prime[j]==0)
        break;
      miu[i*prime[j]]=-miu[i];
    }
  }
  for(int i=1;i<=n;i++)
    miu[i]+=miu[i-1]; 
}
lovelive calc(lovelive n)
{
  if(n<=2e6)
    return miu[n];
  int tmp=sp.query(n*2000+1);
  if(tmp!=-1)
    return tmp;
  lovelive ans=1;
  for(lovelive i=2,r;i<=n;i=r+1)
  {
    r=n/(n/i);
    if(r>n)
      r=n;
    ans-=(r-i+1)*calc(n/i);
  }
  sp.add(n*2000+1,ans);
  return ans;
}
int a[20],cnt;
lovelive f[2020];
lovelive calc2(int n,int k)
{
  return (n/k)*f[k]+f[n%k];
}
lovelive calc3(lovelive n,int k)
{
  if(n<=1)
    return n;
  if(k==1)
    return calc(n);
  int tmp=sp.query(n*2000+k);
  if(tmp!=-1)
    return tmp;
  int p=pre[k];
  lovelive ans=calc3(n/p,k);
  tmp=k;
  while(tmp%p==0)
    tmp/=p;
  ans+=calc3(n,tmp);
  sp.add(n*2000+k,ans); 
  return ans;
}
int gcd(int x,int y)
{
  if(!y)
    return x;
  return gcd(y,x%y);
}
int main()
{
  lovelive n,m,k;
  read(n);read(m);read(k);
  for(int i=1;i<=k;i++)
    f[i]=(gcd(i,k)==1);
  for(int i=1;i<=k;i++)
    f[i]+=f[i-1];
  find_prime(2000000);
  a[0]=1;
  for(int j=1;prime[j]<=k;j++)
    if(k%prime[j]==0)
    {
      a[1<<cnt]=prime[j];
      ++cnt;
    }
  for(int j=1;j<(1<<cnt);j++)
    a[j]=a[j^lowbit(j)]*a[lowbit(j)];
  lovelive ans=0;
  for(lovelive i=1,r;i<=min(n,m);i=r+1)
  {
    r=min(n/(n/i),m/(m/i));
    if(r>min(n,m))
      r=min(n,m);
    ans+=(calc3(r,k)-calc3(i-1,k))*(n/i)*calc2(m/i,k);
  }
  cout<<ans<<"\n";
  return 0;
}
/*
1000000000 1000000000 960
*/ 
View Code

 

posted @ 2018-05-03 15:42  NicoDafaGood  阅读(105)  评论(0编辑  收藏  举报