• gcd套路变换


    gcd套路变换

    GCD

    https://www.luogu.org/problem/P2568

    给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的数对(x,y)有多少对.

    $ 1<=N<=10^7$

    答案是n*n矩阵中每两个数之间gcd==1的数对个数

    考虑把(n imes n)的矩阵分成两部分,即从对角线劈开,设每一部分的答案为ans,则最终答案为$ ans*2 - 对角线上的(即n以内的质数个数)$
    这个把矩阵分成两部分的方法很有用,记一下吧

    [ans = sum_{i = 1}^{n}sum_{j = 1}^{i} isprime[gcd(i, j)] ]

    套路变形,枚举d = gcd(i, j),

    则上述式子等价于

    [ans = sum_{d = 1}^{n}\, isprime[d]\,sum_{i = 1}^{n}sum_{j = 1}^{i}[gcd(i, j) == d] ]

    又因为:

    [i = 2d, 3d, 4d....kd。 j = 2d, 3d....sd ]

    所以有

    [sum_{i = 1}^{n}sum_{j = 1}^{I}[gcd(i, j) == d] = \,\,sum_{i = 1}^{lfloorfrac{n}{d} floor}sum_{j=1}^{i} [gcd(i, j) == 1] ]

    所以如下套路:

    [ans = sum_{d = 1}^{n}isprime[d]sum_{i = 1}^{lfloorfrac{n}{d} floor}sum_{j=1}^{i} [gcd(i, j) == 1] ]

    后面的显然就是(varphi(i)) , 然后算上中间的(sum), 就是前(lfloorfrac{n}{d} floor) 个数的$ varphi $和,求一个前缀和即可

    [ans = sum_{d=1}^{n}isprime[d]*s[frac{n}{i}] ]

    对于d,我们可以直接枚举为质数的情况,因为这题要求的是素数对数,其他不是素数的d对答案都是没有贡献的,所以考虑d为素数的情况,(isprime[d] == 1), 所以一个for累加即可

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N = 10000000+9;
    
    int n;
    long long ans;
    int not_prime[N] = {1, 1}, prime[N], tot, phi[N];
    long long sum[N];
    
    void L_S() {
    	phi[1] = 1;
    	for(int i = 2; i <= n; i++) {
    		if(!not_prime[i]) {
    			prime[++tot] = i;
    			phi[i] = i-1;
    		}
    		for(int j = 1; j <= tot && i*prime[j] <= n; j++) {
    			not_prime[i*prime[j]] = 1;
    			if(i % prime[j] == 0) {
    				phi[i*prime[j]] = phi[i]*prime[j];
    				break;
    			}
    			phi[i*prime[j]] = phi[i]*(prime[j]-1);
    		}
    	}
    	for(int i = 1; i <= n; i++) sum[i] = (long long)sum[i-1]+phi[i];
    }
    
    int main() {
    	scanf("%d", &n);
    	L_S();
    	for(int i = 1;i <= tot; i++) ans += sum[n/prime[i]];//prime[i]即为d 
    	printf("%lld", ans*2-tot);
    }
    

    注:不求数量,求值也是这样的套路,只不过要乘以d

    [SDOi2012]Longge的问题

    求 ∑gcd(i, N)(1<=i <=N)

    0<N<=2^32

    枚举d = gcd(i, n)

    原式进行如下变换:

    [sum_{d=1}^{n} d sum_{i=1}^{n}[gcd(i, n) == d] ]

    再次套路

    [sum_{d=1}^{n} d sum_{i=1}^{lfloorfrac{n}{d} floor}[gcd(i, frac{n}{d}) == 1] ]

    后面的还是上面的那个

    [sum_{d=1}^{n} d imes varphi(lfloorfrac{n}{d} floor) ]

    **我们对于每个d,要求在区间$[1, lfloorfrac{n}{d} floor ] $ 内有多少i使得gcd(i,n/d)=1,设求出有x个i,那么对答案的贡献就是$ d imes x$ **

    这题和上题不一样,不是对于素数求这个式子,所以我们需要枚举n的因数, 所以单个求phi即可。

    这种枚举因数的方法可以记一下(个人感觉有点上面分矩阵的思想

    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    #define ll long long
    
    ll el_phi(ll n) {
    	ll m = sqrt(n+0.5);
    	ll ans = n;
    	for(ll i = 2; i <= m; i++) if(n%i == 0) {
    		ans = ans/i*(i-1);
    		while(n%i == 0) n /= i;
    	}
    	if(n > 1) ans = ans/n*(n-1);
    	return ans;
    }
    ll n, ans;
    
    int main() {
    	scanf("%lld", &n);
    	ll m = sqrt(n+0.5);
    	for(ll i = 1; i <= m; i++) if(n % i == 0) {
    		ans += i*el_phi(n/i);
    		if(i*i != n) ans += el_phi(i)*(n/i);//i * i != n时i和n/i都是n的因数 
    	}
    	printf("%lld", ans);
    	return 0;
    }
    

    [SDOI2008]仪仗队

    与第一题双倍经验

    https://www.luogu.org/problem/P2158

    求$ Σ(1<=i<=n)Σ(1<=j<=i)[gcd(i,j)==1] $

    得,原式 = $ Σ(1<=i<=n) φ(i) $

    做法: 欧拉线性筛

    #include<cstdio>
    #include<algorithm>
    #include<set>
    using namespace std;
    const int MAXN = 40000+9;
    
    int n,ans;
    int phi[MAXN], not_prime[MAXN], prime[MAXN], tot;
    
    void L_S() {
    	phi[1] = 1;
    	for(int i = 2; i <= n; i++) {
    		if(!not_prime[i]) {
    			prime[++tot] = i;
    			phi[i] = i-1;
    		}
    		for(int j = 1; i*prime[j] <= n; j++) {
    			not_prime[prime[j]*i] = 1;
    			if(i % prime[j] == 0) {
    				phi[i*prime[j]] = phi[i]*prime[j];
    				break;
    			}
    			phi[i*prime[j]] = phi[i]*(prime[j]-1);
    		}
    	}
    }
    
    int main() {
    	scanf("%d",&n);
    	if(n == 0 || n == 1) {
    		printf("0");
    		return 0;
    	}
    	n--;
    	L_S();
    	for(int i = 1; i <= n; i++) ans += phi[i];
    	printf("%d",(ans<<1)-1+2);
    }
    

    SP5971 LCMSUM - LCM Sum

    求$ sum_{i=1}^{n} ; lcm(i,n)$

    $ 1<=T<=300000 , 1 <= n <= 1000000 $

    原式进行如下变形:

    [sum_{i=1}^{n} frac{i imes n}{gcd(i, n)} ]

    套路变形,注意:这里是要求求值

    [sum_{dmid n} frac{n}{d}sum_{i=1}^{n}i\,[gcd(i, n)==d] ]

    注意这一步,上面是(i), 变除法的时候要注意

    [sum_{dmid n}frac{n}{d} sum_{i=1}^{lfloorfrac{n}{d} floor}i imes d[gcd(i, lfloorfrac{n}{d} floor) == 1] ]

    结合:[1,n]范围内 与n互质的数 的和:(φ(n)∗n)/2,化简得:

    [n imes sum_{dmid n} (varphi(frac{n}{d}) imes frac{n}{d})/2 ]

    即:

    [n imes sum_{dmid n} frac{varphi(d) imes d}{2} ]

    for(int i = 1; i <= N; i++)
        for(int j = i; j <= N; j+=i) //i为j的因数, 所以是'f[j] +=' 
    	f[j] += i==1 ? 1 : 1ll*phi[i]*i/2;//i==1时对f的贡献应该是1
    
  • 相关阅读:
    AOP从静态代理到动态代理 Emit实现
    云计算仿真工具CloudSim介绍和使用
    SSH框架中配置log4j的方法
    SSH常见面试题
    第一章
    shell 生成目录的树状视图、生成文件及子目录的汇总信息
    shell拼写检查,利用Linux字典
    SHELL:多文件的重命名和移动
    sort
    tr1
  • 原文地址:https://www.cnblogs.com/tyner/p/11853212.html
Copyright © 2020-2023  润新知