zoukankan      html  css  js  c++  java
  • BZOJ4652 [Noi2016]循环之美

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

    本文作者:ljh2000
    作者博客:http://www.cnblogs.com/ljh2000-jump/
    转载请注明出处,侵权必究,保留最终解释权!

     

    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 也只计数一次。
     
     
    正解:莫比乌斯反演+杜教筛
    解题报告:
      这道题可以说是一道莫比乌斯神题了…
      题目难度不小,不过也挺优美的,做得多了就很自然能想到。
      我懒得写题解啦,LaTex写数学公式写的蛋疼...附上我校神犇LCF的题解:LCF
      话说NOI2016还真够良心的,推一个很常规的莫比乌斯反演式子就可以get84分,暴力给足了…
      要我在考场上,肯定是打了84分暴力走人的…
      最后的16分感觉比84分还要难拿…
      到了84分之后, 那个式子有两个部分显然可以做到根号,其余的部分想办法继续优化,直到复杂度可以接受。
      最后化归到求莫比乌斯函数的前缀和上,就变成杜教筛裸题了呀。
      如果你不会杜教筛,左转学习一发吧:杜教筛
      (我的代码好学易懂)
      讲一讲我做这道题的时候的优化和细节上的问题:显然g函数的计算可以记忆化,对于k的质因数的处理时,可以预处理完之后只考虑还剩几个质因数没有除即可,不必具体算出来。
      同时,杜教筛的时候,不能用平时惯用的用分母表示分数!
      因为分子不一定是n还有可能是m,为了避免对n、m的讨论,我们只能把这个变成hash查找...
      均摊复杂度没有区别啦...
     
      
    //It is made by ljh2000
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <ctime>
    #include <vector>
    #include <queue>
    #include <map>
    #include <set>
    #include <string>
    #include <complex>
    using namespace std;
    typedef long long LL;
    const int MAXN = 300011;
    const int MOD = 10007;
    const int mod = 3000007;
    const int MAXM = 5000011;
    int n,m,k,block,divk[2017],scnt,f[2017],prime[MAXN],cnt;
    int hash[2017][MOD+12],ecnt,to[MAXM],next[MAXM];
    bool vis[MAXN];
    LL ans,w[MAXM],mobius[MAXN];
    inline int gcd(int x,int y){ if(y==0) return x; return gcd(y,x%y); }
    inline LL getf(int x){ return (LL)(x/k)*f[k]+f[x%k]; }
    inline int getint(){
        int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
        if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
    }
    
    inline void init(){
    	//block=(int)pow(n,0.6);
    	block=300000;
    	mobius[1]=1;
    	//线性筛和预处理莫比乌斯函数
    	for(int i=2;i<=block;i++) {
    		if(!vis[i]) { mobius[i]=-1; prime[++cnt]=i; }
    		for(int j=1;j<=cnt && i*prime[j]<=block;j++) {
    			vis[i*prime[j]]=1;
    			if(i%prime[j]==0) { mobius[i*prime[j]]=0; break; }
    			mobius[i*prime[j]]=-mobius[i];
    		}
    	}
    	for(int i=2;i<=block;i++) mobius[i]+=mobius[i-1];
    
    	int now=k;
    	for(int i=2;i<=k;i++) {//预处理k的质因数
    		if(now%i!=0) continue;
    		scnt++; divk[scnt]=i;
    		while(now%i==0) now/=i;
    		if(now==1) break;
    	}
    
    	for(int i=1;i<=k;i++) {//预处理f
    		f[i]=f[i-1];
    		if(gcd(i,k)==1) f[i]++;
    	}
    }
    
    int first[mod+12],ECNT;
    struct edge{ int next,to; LL w; }e[MAXM];
    
    inline LL calc(int x){//计算莫比乌斯函数前缀和
    	if(x<=block) return mobius[x];
    	int cc=x%mod;
    	for(int i=first[cc];i;i=e[i].next) if(e[i].to==x) return e[i].w;
    	LL tt=1; int nex; 
    	for(int i=2;i<=x;i=nex+1) {//!!!
    		nex=x/(x/i);
    		tt-=(LL)calc(x/i)*(nex-i+1);
    	}
    	e[++ECNT].next=first[cc]; first[cc]=ECNT; e[ECNT].to=x; e[ECNT].w=tt;
    	return tt;
    }
    
    inline LL getg(int x,int kcnt){//计算g(n,k)的值,可以记忆化搜索
    	if(x<=1) return x; if(kcnt==0) return calc(x);
    	int cc=x%MOD;
    	for(int i=hash[kcnt][cc];i;i=next[i]) if(to[i]==x) return w[i];
    	LL nowans=0; nowans=getg(x,kcnt-1)+getg(x/divk[kcnt],kcnt);
    	next[++ecnt]=hash[kcnt][cc]; hash[kcnt][cc]=ecnt; to[ecnt]=x; w[ecnt]=nowans;
    	return nowans;
    }
    
    inline void work(){
    	n=getint(); m=getint(); k=getint();	init();
    	int lim=min(n,m),nex;
    	for(int i=1;i<=lim;i=nex+1) {
    		nex=min(n/(n/i),m/(m/i));
    		ans+=(LL)getf(m/i)*(n/i)*(getg(nex,scnt)-getg(i-1,scnt));
    	}
    	printf("%lld",ans);
    }
    
    int main()
    {
        work();
        return 0;
    }
    

      

     
  • 相关阅读:
    How to get IWin32Window for Show/ShowDialog in Excel
    SSL 改变端口 ReportViewer 超级慢
    RportViewer(20121023) 参数引起的异常
    IKVM.NET[Java to C#]
    knockoutjs(03) ko + jquery ui
    我的四色
    vsto to fill data use listobject fill
    sinsspp 插件和主题安装
    自动化测试
    HTTP Error 500.19
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6395469.html
Copyright © 2011-2022 走看看