zoukankan      html  css  js  c++  java
  • 【loj


    description

    牛牛是一个热爱算法设计的高中生。在他设计的算法中,常常会使用带小数的数进行计算。牛牛认为,如果在 (k) 进制下,一个数的小数部分是纯循环的,那么它就是美的。

    现在,牛牛想知道:对于已知的十进制数 (n)(m),在 (k) 进制下,有多少个数值上互不相等的纯循环小数,可以用分数 (frac{x}{y}) 表示,其中 (1leq x leq n, 1 leq y leq m),且 (x, y) 是整数。

    一个数是纯循环的,当且仅当其可以写成以下形式:

    [a.dot{c_1}c_2c_3dots c_{p-1}dot{c_{p}} ]

    其中,(a) 是一个整数,(pgeq 1);对于 (1leq i leq p)(c_i)(k) 进制下的一位数字。

    例如,在十进制下,(0.45454545cdots=0.dot{4}dot{5}) 是纯循环的,它可以用 (frac{5}{11})(frac{10}{22}) 等分数表示;在十进制下,(0.1666666cdots=0.1dot{6}) 则不是纯循环的,它可以用 (frac{1}{6}) 等分数表示。

    需要特别注意的是,我们认为一个整数是纯循环的,因为它的小数部分可以表示成 (0) 的循环或是 (k-1) 的循环;而一个小数部分非 (0) 的有限小数不是纯循环的。

    solution

    考虑既约分数 (frac{j}{i}) 是纯循环的等价条件:存在 (p) 使得 (j imes k^p=j mod i)

    由于 (j, i) 互质,那么上述条件等价于 (k, i) 也互质。因此答案为:

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

    首先套路反演:

    [sum_{d|k}mu(d)sum_{p}mu(p) imeslfloorfrac{n}{p} floor imeslfloorfrac{m}{{ m lcm}(d,p)} floor ]

    二度反演:

    [sum_{a|b|d|k}mu(d)sum_{p'}mu(b imes p') imeslfloorfrac{n}{b imes p'} floor imeslfloorfrac{m imes a}{d imes b imes p'} floor ]

    枚举四元组 ((a, b, d, k)),记 (n' = lfloorfrac{n}{b} floor, m'=lfloorfrac{m imes a}{d imes b} floor),则我们相当于求:

    [sum_{p'}mu(b imes p') imeslfloorfrac{n'}{p'} floor imeslfloorfrac{m'}{p'} floor ]

    后面部分很明显可以分块。考虑怎么快速求 (S_{b}(n) = sum_{i=1}^{n}mu(b imes i)),三度反演:

    [egin{aligned} sum_{i=1}^{n}mu(b imes i) &=sum_{i=1}^{n}mu(b) imesmu(i) imes[gcd(b,i)=1] \ &=sum_{d|b}mu(b) imesmu(d) imes S_{d}(lfloorfrac{n}{d} floor) end{aligned} ]

    (b = 1) 时直接杜教筛,否则就做如上的过程。不难发现 (b ot=1) 时转移无环。

    对于最后一步 (b ot = 1) 的时间复杂度分析:一共会有 (O(d(k) imessqrt{n})) 个状态,每个状态有 (O(d(k))) 种转移。所以可以通过。

    accepted code

    #include <cmath>
    #include <cstdio>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    typedef long long ll;
    
    const int MAXK = 2000;
    const int MAXN = 1000000;
    const ll INF = ll(1E12);
    
    int mu[MAXN + 5], prm[MAXN + 5], pcnt;
    bool nprm[MAXN + 5];
    
    vector<int>dv[MAXK + 5];
    void init() {
    	for(int i=1;i<=MAXK;i++)
    		for(int j=i;j<=MAXK;j+=i)
    			dv[j].push_back(i);
    			
    	mu[1] = 1;
    	for(int i=2;i<=MAXN;i++) {
    		if( !nprm[i] ) prm[++pcnt] = i, mu[i] = -1;
    		for(int j=1;i*prm[j]<=MAXN;j++) {
    			nprm[i*prm[j]] = true;
    			if( i % prm[j] == 0 ) {
    				mu[i*prm[j]] = 0;
    				break;
    			}
    			else mu[i*prm[j]] = -mu[i];
    		}
    	}
    }
    
    ll f[20][MAXN + 5]; int id1[MAXN + 5], id2[MAXN + 5], tot;
    int id(int x) {return (x <= MAXN ? id1[x] : id2[INF / x]);}
    
    ll smu[20][MAXN + 5]; int A[MAXK + 5], B[20], cnt;
    ll sum(int x, int n) {
    	ll &ans = f[x][id(n)];
    	if( ans != INF ) return ans;
    	if( n <= MAXN ) return ans = smu[x][n];
    	
    	if( x == 1 ) {
    		ans = 1;
    		for(int i=2,j;i<=n;i=j+1) {
    			int p = (n / i); j = n / p;
    			ans -= (j - i + 1)*sum(x, p);
    		}
    	} else {
    		ans = 0;
    		for(unsigned i=0;i<dv[B[x]].size();i++) {
    			int d = dv[B[x]][i];
    			ans += mu[d]*sum(A[d], n / d);
    		}
    		ans *= mu[B[x]];
    	}
    	return ans;
    }
    
    void get_id(int n, int m) {
    	tot = 0;
    	for(int i=1;i<=n&&i<=m;i++) {
    		i = min(n / (n / i), m / (m / i));
    		if( i <= MAXN ) id1[i] = (++tot);
    		else id2[INF / i] = (++tot);
    		
    		for(int j=1;j<=cnt;j++) f[j][tot] = INF;
    	}
    }
    ll solve(int b, int n, int m) {
    	get_id(n, m); ll ret = 0;
    	for(int i=1,j;i<=n&&i<=m;i=j+1) {
    		int p = (n / i), q = (m / i);
    		j = min(n / p, m / q);
    		ret += 1LL*(sum(A[b], j) - sum(A[b], i - 1))*p*q;
    	}
    	return ret;
    /*
    	ll ret = 0;
    	for(int p=1;p<=n;p++)
    		ret += 1LL*mu[p*b]*(n / p)*(m / p);
    	return ret;
    */
    }
    
    bool tag[MAXN + 5];
    void get(int k) {
    	for(unsigned i=0;i<dv[k].size();i++)
    		if( mu[dv[k][i]] ) B[++cnt] = dv[k][i], A[dv[k][i]] = cnt;
    	for(int i=1;i<=cnt;i++) {
    		tag[1] = true, smu[i][1] = 1;
    		for(int j=2;j<=MAXN;j++) {
    			if( !nprm[j] ) tag[j] = (B[i] % j);
    			smu[i][j] = smu[i][j - 1] + (tag[j] ? mu[j] : 0);
    			for(int p=1;j*prm[p]<=MAXN;p++) {
    				nprm[j*prm[p]] = true, tag[j*prm[p]] = (tag[j] && tag[prm[p]]);
    				if( j % prm[p] == 0 ) break;
    			}
    		}
    		for(int j=1;j<=MAXN;j++) smu[i][j] *= mu[B[i]];
    	}
    }
    int main() {
    	int n, m, k;
    	scanf("%d%d%d", &n, &m, &k);
    	init(), get(k);
    	
    	ll ans = 0;
    	for(unsigned i1=0;i1<dv[k].size();i1++) {
    		int d = dv[k][i1]; if( !mu[d] ) continue;
    		for(unsigned i2=0;i2<dv[d].size();i2++) {
    			int b = dv[d][i2];
    			for(unsigned i3=0;i3<dv[b].size();i3++) {
    				int a = dv[b][i3];
    				
    				ans += mu[d] * mu[b / a] * solve(b, n / b, 1LL * m * a / b / d);
    			}
    		}
    	}
    	printf("%lld
    ", ans);
    }
    

    details

    虽然 (summu) 不会溢出,但中间过程可能会溢出,所以还是要开 long long。

  • 相关阅读:
    spark rdd--分区理解
    2020-05-03 助教一周小结(第十二周)
    2020-04-26 助教一周小结(第十一周)
    2020-04-19 助教一周小结(第十周)
    2020-04-12 助教一周小结(第九周)
    2020-04-05 助教一周小结(第八周)
    2020-03-29 助教一周小结(第七周)
    2020-03-22 助教一周小结(第六周)
    内网渗透思考(实践)
    2020-03-15 助教一周小结(第五周)
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/13139927.html
Copyright © 2011-2022 走看看