zoukankan      html  css  js  c++  java
  • [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
    

    Solution

    (k)进制下(x/y)为循环小数,即:

    [ig[frac{xk^l}{y}ig]=ig[frac{x}{y}ig] ]

    其中中括号表示小数部分,(l)(k)进制下循环节长度。

    中括号展开可得:

    [frac{xk^l}{y}-lfloorfrac{xk^l}{y} floor=frac{x}{y}-lfloorfrac{x}{y} floor\ xk^l-ylfloorfrac{xk^l}{y} floor=x-ylfloorfrac{x}{y} floor ]

    考虑取整那一项不好处理,可以玩一波骚操作,两边对(y)取模:

    [xk^lequiv xpmod{y} ]

    因为((x,y)=1),所以两边除掉:

    [k^lequiv1pmod{y} ]

    可得:

    [(k,y)=1 ]

    所以答案可以形式化的写成:

    [sum_{i=1}^nsum_{j=1}^m[(i,j)=1][(j,k)=1] ]

    然后尝试着对([(i,j)=1])进行莫比乌斯反演:

    [egin{align} ans=&sum_{i=1}^nsum_{j=1}^m[(j,k)=1]sum_{d|i&d|j}mu(d)\ =&sum_{d=1}^{min(n,m)}mu(d)[(d,k)=1]lfloorfrac{n}{d} floorsum_{j=1}^{lfloorfrac{m}{d} floor}[(j,k)=1] end{align} ]

    此处省略了中间交换求和符号之类的过程。

    然后考虑下这个式子后面一块,设:

    [f(n)=sum_{i=1}^n[(i,k)=1] ]

    考虑分成几块,每块长度都是(k),显然:

    [f(n)=lfloorfrac{n}{k} floor f(k)+f(nmod k) ]

    再考虑下答案的前半段,由于后两项要数论分块,所以要求前两项的前缀和,设:

    [g(n,k)=sum_{d=1}^nmu(d)[(d,k)=1] ]

    对后面莫比乌斯反演下:

    [egin{align} g(n,k)=&sum_{d=1}^nmu(d)sum_{t|d&t|k}mu(t)\ =&sum_{t=1}^nmu(t)[t|k]sum_{d=1}^{lfloorfrac{n}{t} floor}mu(dt) end{align} ]

    然后最玄学的一步来了,,若想要使(mu(dt) e 0),显然((d,t)=1),此时(mu(dt)=mu(d)mu(t)),所以后面一项可以写成:

    [egin{align} g(n,k)=&sum_{t=1}^nmu(t)[t|k]sum_{d=1}^{lfloorfrac{n}{t} floor}mu(d)mu(t)[(d,t)=1]\ =&sum_{t=1}^nmu^2(t)[t|k]sum_{d=1}^{lfloorfrac{n}{t} floor}mu(d)[(d,t)=1]\ =&sum_{t=1}^nmu^2(t)[t|k]g(lfloorfrac{n}{d} floor,t)\ =&sum_{t|k}mu^2(t)g(lfloorfrac{n}{d} floor,t)\ end{align} ]

    这是个递归的形式,预处理下(k)以内所有数的约数,直接记忆化暴力算就好了。

    对于(k=1)的情况,(g)就变成了(sum_{i=1}^n mu(i)),这个可以杜教筛出来。

    答案可以写成:

    [ans=sum_{d=1}^{min(n,m)}mu(d)[(d,k)=1]lfloorfrac{n}{d} floor f(lfloorfrac{m}{d} floor) ]

    后面数论分块就行了。

    #pragma GCC optimize(3)
    #include<bits/stdc++.h>
    using namespace std;
    
    #ifdef ONLINE_JUDGE
    #define getchar() ((p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin)),p1==p2)?EOF:*p1++)
    #endif
    
    namespace fast_IO {
    	char buf[1<<21],*p1=buf,*p2=buf;
    
    	template <typename T> inline void read(T &x) {
    		x=0;T f=1;char ch=getchar();
    		for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    		for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
    	}
    	template <typename T,typename... Args> inline void read(T& x,Args& ...args) {
    		read(x),read(args...);
    	}
    
    	char buf2[1<<21],a[80];int p,p3=-1;
    
    	inline void flush() {fwrite(buf2,1,p3+1,stdout),p3=-1;}
    	template <typename T> inline void write(T x) {
    		if(p3>(1<<20)) flush();
    		if(x<0) buf2[++p3]='-',x=-x;
    		do {a[++p]=x%10+48;} while(x/=10);
    		do {buf2[++p3]=a[p];} while(--p);
    		buf2[++p3]='
    ';
    	}
    	template <typename T,typename... Args> inline void write(T x,Args ...args) {
    		write(x),write(args...);
    	}
    }
    
    using fast_IO :: read;
    using fast_IO :: write;
    using fast_IO :: flush;
    
    #define ll long long 
    
    const int maxn = 5e3+10;
    const int N = 5e6+10;
    
    int pri[N],tot,mu[N],vis[N],F[maxn],Mu[N];
    
    void sieve() {
    	mu[1]=1;
    	for(int i=2;i<N;i++) {
    		if(!vis[i]) pri[++tot]=i,mu[i]=-1;
    		for(int j=1;j<=tot&&i*pri[j]<N;j++) {
    			vis[i*pri[j]]=1;
    			if(i%pri[j]==0) break;
    			mu[i*pri[j]]=-mu[i];
    		}
    	}
    	for(int i=1;i<N;i++) Mu[i]=Mu[i-1]+mu[i];
    }
    
    map<int ,int > mp;
    
    int sum_mu(int n) {
    	if(n<N) return Mu[n];
    	if(mp[n]) return mp[n];
    	int res=1,T=2;
    	while(T<=n) {
    		int pre=T;T=n/(n/T);
    		res=res-(T-pre+1)*sum_mu(n/T);T++;
    	}
    	return mp[n]=res;
    }
    
    int dv[2001][201];
    map<int ,int > G[2001];
    
    int g(int n,int k) {
    	if(k==1) return sum_mu(n);int ans=0;
    	if(G[k][n]) return G[k][n];
    	if(!n) return 0;
    	for(int i=1;i<=dv[k][0];i++)
    		ans+=abs(mu[dv[k][i]])*g(n/dv[k][i],dv[k][i]);
    	return G[k][n]=ans;
    }
    
    int n,m,k;
    
    int f(int x) {return F[k]*(x/k)+F[x%k];}
    
    int main() {
    	read(n,m,k);
    	for(int i=1;i<=k;i++) F[i]=F[i-1]+(__gcd(i,k)==1);
    	for(int i=1;i<=k;i++)
    		for(int j=1;j<=i;j++)
    			if(i%j==0) dv[i][++dv[i][0]]=j;
    	sieve();int T=1;ll ans=0;
    	while(T<=n&&T<=m) {
    		int pre=T;T=min(n/(n/T),m/(m/T));
    		ans=ans+1ll*(g(T,k)-g(pre-1,k))*(n/T)*f(m/T);T++;
    	}write(ans);
    	flush();
    	return 0;
    }
    
  • 相关阅读:
    c++ struct 使用
    c++数组、字符串操作
    c++ List、Vector、Stack、Queue使用
    十三、哈希表
    十二、234树
    十一、红黑树
    十、哈夫曼编码
    九、二叉树
    八、高级排序
    七、递归
  • 原文地址:https://www.cnblogs.com/hbyer/p/10214066.html
Copyright © 2011-2022 走看看