zoukankan      html  css  js  c++  java
  • P1587 [NOI2016]循环之美

    P1587 [NOI2016]循环之美

    显然只用统计最简分数的情况,否则会算重。

    原题等价于:(1le i le n,1le j le m) 有几个最简分数 (dfrac{i}{j})(k) 进制下是纯循环小数

    (color{black}{ exttt {c}}color{red}{ exttt {yn2006}})一个最简分数 (dfrac{x}{y})(k) 进制下是纯循环小数当且仅当 (gcd(y,k)=1)

    其实一开始也是类比十进制纯循环小数去推,但是没成功/kk

    后来 (color{black}{ exttt {c}}color{red}{ exttt {yn2006}}) 随手一推就推出来还把我D了一顿。。。

    证明:

    若一个分数 (dfrac{x}{y})(k) 进制下是纯循环小数,那么必然存在正整数 (l) 满足 (k^l imesdfrac{x}{y}-lfloor k^l imesdfrac{x}{y} floor = dfrac{x}{y})

    两边同时乘上 (y)(x imes k^l-y imes lfloor k^l imesdfrac{x}{y} floor=x),由于两边相等,那么同时 (mod y) 也相等。

    化简得(x imes k^lequiv x pmod {y}) ,因为 (gcd(x,y)=1) ,所以可以消去 (x) , 即 (k^lequiv 1pmod y)

    显然,若 (gcd(k^l,y)=1) 那么 (gcd(k,y)=1)


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

    不会了。。。。

    (kle2000???)

    [sum_{i=1}^{n}sum_{j=1}^{m}[gcd(i,j)==1] [gcd(j,k)==1]\ =sum_{j=1}^{m}[gcd(j,k)==1]sum_{i=1}^{n}[gcd(i,j)==1]\ =sum_{j=1}^{m}[gcd(j,k)==1]sum_{i=1}^{n}sum_{d|gcd(i,j)}mu(d)\ =sum_{d=1}^{n}mu(d)sum_{j=1}^{frac{m}{d}}[gcd(jd,k)==1]sum_{i=1}^{frac{n}{d}}1\ =sum_{d=1}^{n}mu(d)dfrac{n}{d}sum_{j=1}^{frac{m}{d}}[gcd(jd,k)==1]\ =sum_{d=1}^{n}[gcd(d,k)==1]mu(d)dfrac{n}{d}sum_{j=1}^{frac{m}{d}}[gcd(j,k)==1] ]

    先看后半部分 (sumlimits_{i=1}^{n}[gcd(i,k)==1]) ,非常一眼:

    [sum_{i=1}^{n}[gcd(i,k)==1]=lfloordfrac{n}{k} floor imesvarphi(k)+s(n\%k) ]

    其中 (s(n)=sumlimits_{i=1}^{n}[gcd(i,k)==1])(Huge kle 2000!!!)(O(klog k)) 记个前缀和就可以预处理出来。

    这部分就可以 (O(1)) 啦!

    看另一部分:

    [sum_{i=1}^{n}[gcd(i,k)==1]mu(i)\ =sum_{i=1}^{n}mu(i)sum_{d|gcd(i,k)}mu(d)\ =sum_{d|k}mu(d)sum_{i=1}^{frac{n}{d}}mu(id) ]

    (gcd(i,d) ot=1) 时, (id) 必有平方因子,即 (mu(id)=0)

    所以只用计算 (gcd(i,d)=1) 时的情况。

    由于 (mu) 是积性函数,在 (i,d) 互质的情况下 (mu(id)=mu(i)mu(d))

    带回去:

    [=sum_{d|k}mu(d)sum_{i=1}^{frac{n}{d}}[gcd(i,d)==1]mu(id)\ =sum_{d|k}mu(d)sum_{i=1}^{frac{n}{d}}[gcd(i,d)==1]mu(i)mu(d)\ =sum_{d|k}mu(d)^2sum_{i=1}^{frac{n}{d}}[gcd(i,d)==1]mu(i) ]

    (color{black}{ exttt {c}}color{red}{ exttt {yn2006}}) 告诉我可以递归。

    恍然大悟.jpg

    (f(n,k)=sumlimits_{i=1}^{n}[gcd(i,k)==1]mu(i))

    那么有 (f(n,k)=sumlimits_{d|k}mu(d)^2f(dfrac{n}{d},d)) ,可以递归了

    那个 (mu(d)^2) 怎么办啊?

    (color{black}{ exttt {c}}color{red}{ exttt {yn2006}}) 怒斥:(Huge kle2000!!!)

    于是暴力 (sqrt{k}) 跑因数的复杂度变对了qwq

    边界?

    (n=0) 的时候显然是 (0)

    (k=1) 的时候是 (sum_{i=1}^{n}mu(i)) ,杜教筛。

    然后递归就好了!

    一路递归回去找式子.jpg

    做完了诶!整除分块一下式子就没了!

    另外,递归的时候记得加记忆化,这样复杂度就是 (O(operatorname{d}(n)sqrt{n}+n^{frac{2}{3}}))

    其中 (operatorname{d}(i)) 表示 (i) 的约数个数

    给两个大样例以供调试

    Input#1
    1000000000 99999999 1926
    Output#1
    30114906314949722
    
    Input#2
    904 791040170 780
    Output#2
    168263165839
    
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef double db;
    #define pb(x) push_back(x)
    #define mkp(x,y) make_pair(x,y)
    //#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    //char buf[1<<21],*p1=buf,*p2=buf;
    inline int read() {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
    	return x*f;
    }
    const int N = 1000005;
    const int C = 1000000;
    const int K = 2005;
    int n, m, k;
    int mu[N], sm[N], pri[N / 9], cnt, s[K];
    bool vis[N];
    LL ans;
    unordered_map <int, int> _mu, dp[K];
    int gcd(int x, int y) {return !y ? x : gcd(y, x % y);}
    void init() {
    	mu[1] = 1;
    	for (int i = 2; i <= C; ++ i) {
    		if (!vis[i]) pri[++cnt] = i, mu[i] = -1;
    		for (int j = 1; j <= cnt && i * pri[j] <= C; ++ j) {
    			vis[i * pri[j]] = 1;
    			if (i % pri[j] == 0) break;
    			mu[i * pri[j]] = - mu[i];
    		}
    	}
    	for (int i = 1; i <= C; ++ i) sm[i] = mu[i] + sm[i - 1];
    	for (int i = 1; i <= k; ++ i) s[i] = s[i - 1] + (gcd(i, k) == 1);
    }
    int smu(int x) {
    	if (x <= C) return sm[x];
    	if (_mu[x]) return _mu[x];
    	int res = 1;
    	for (int l = 2, r; l <= x; l = r + 1)
    		r = x / (x / l), res -= (r - l + 1) * smu(x / l);
    	return _mu[x]=res;
    }
    int h(int x) {return s[k] * (x / k) + s[x % k];}//for i 1 x :[gcd(i,k)=1]
    int g(int n, int k) {
    	if (!n) return 0;
    	if (k == 1) return dp[k][n] = smu(n);
    	if (dp[k][n]) return dp[k][n];
    	int res = 0;
    	for (int i = 1; i * i <= k; ++ i) {
    		if (k % i == 0) {
    			if (mu[i]) res += mu[i] * mu[i] * g(n / i, i);
    			if (mu[k / i] && i * i != k) res += mu[k / i] * mu[k / i] * g(n / (k / i), k / i);
    		}
    	}
    	return dp[k][n] = res;
    }
    signed main(){
    	n = read(), m = read(), k = read(), init();
    	for (int l = 1, r, mx = min(n, m); l <= mx; l = r + 1) {
    		r = min(n / (n / l), m / (m / l));
    		ans += 1ll * (n / l) * h(m / l) * (g(r, k) - g(l - 1, k));
    	}
    	printf("%lld
    ", ans);
    	return 0;
    }
    

    整除分块写挂身败名裂*114514

    前缀和写挂身败名裂*1919810

    路漫漫其修远兮,吾将上下而求索
  • 相关阅读:
    网络流24题
    可持久化Treap
    后缀平衡树
    bzoj2561-最小生成树
    poj3164-Command Network
    最小树形图
    hdu2121-Ice_cream’s world II
    线性处理逆元
    bzoj3992-序列统计
    JavaScript 类型转换
  • 原文地址:https://www.cnblogs.com/zzctommy/p/13771623.html
Copyright © 2011-2022 走看看