zoukankan      html  css  js  c++  java
  • [BZOJ 2820] YY的gcd(莫比乌斯反演)

    [BZOJ 2820] YY的gcd(莫比乌斯反演+数论分块)

    题面

    给定N, M,求(1leq xleq N, 1leq yleq M)且gcd(x, y)为质数的(x, y)有多少对。q组询问

    分析

    我们要求的是

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

    (大写P表示质数集合)

    根据(kgcd(i,j)=gcd(ki,kj)),

    [原式=sum_{p in P} sum_{i=1}^{lfloor n/p floor} sum_{j=1}^{lfloor m/p floor} [gcd(i,j)=1] ]

    又根据莫比乌斯反演里的一个常用结论(证明见 BZOJ 2301)

    [sum_{i=1}^n sum_{j=1}^m [gcd(i,j)=1]= sum_{d=1}^{min(n,m)} mu(d ) lfloor frac{n}{d} floor lfloor frac{m}{d} floor ]

    [原式=sum_{p in P} sum_{d=1}^{min( lfloor n/p floor, lfloor m/p floor)} mu(d) lfloor frac{n}{pd} floor lfloor frac{m}{pd} floor ]

    (T=pd),则(d=frac{T}{p})

    改变求和顺序,

    [原式=sum_{T=1}^{min(n,m)} sum_{p|t cap p in P} lfloor frac{n}{T} floor lfloor frac{m}{T} floor mu(frac{T}{p}) ]

    [=sum_{T=1}^{min(n,m)}) lfloor frac{n}{T} floor lfloor frac{m}{T} floor sum_{p|t cap p in P} mu(frac{T}{p}) ]

    (g(n)=sum_{p|n cap p in P } mu(frac{n}{p}))

    [原式=sum_{T=1}^{min(n,m)}) lfloor frac{n}{T} floor lfloor frac{m}{T} floor g(T) ]

    前面的部分可以数论分块求解,考虑如何快速求出(g(T))

    对于每个质数(p),我们从1开始枚举(j),并保证(jp leq n),然后用(mu(j))更新(g(jp))的值。

    由于(1/1+1/2+1/3+...+1/n=O(log n)),每次更新的复杂度是均摊(O(log n))的,而1~n的质数约有(frac{n}{ln n})个,所以预处理g函数的总时间复杂度为(O(n))

    总时间复杂度(O(n+qsqrt n))

    代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define maxn 10000000
    using namespace std;
    typedef long long ll;
    int t,n,m;
    int cnt;
    int prime[maxn+5];
    bool vis[maxn+5];
    int mu[maxn+5];
    ll g[maxn+5];
    ll sumg[maxn+5];
    void sieve(int n){
    	mu[1]=1;
    	for(int i=2;i<=n;i++){
    		if(!vis[i]){
    			mu[i]=-1;
    			prime[++cnt]=i;
    		}
    		for(int j=1;j<=cnt&&i*prime[j]<=n;j++){
    			vis[i*prime[j]]=1;
    			if(i%prime[j]==0){
    				mu[i*prime[j]]=0;
    				break;
    			}else mu[i*prime[j]]=-mu[i];
    		}
    	}
    	for(int i=1;i<=cnt;i++){
    		for(int j=1;j*prime[i]<=n;j++){
    			g[prime[i]*j]+=mu[j];
    		}
    	}
    	for(int i=1;i<=n;i++){
    		sumg[i]=sumg[i-1]+g[i];
    	}
    } 
    
    int cas;
    ll calc(int n,int m){
    	int nn=min(n,m);
    	ll ans=0;
    	for(int l=1,r;l<=nn;l=r+1){
    		r=min(n/(n/l),m/(m/l));
    		ans+=(sumg[r]-sumg[l-1])*(n/l)*(m/l);
    	}
    	return ans;
    }
    int main(){
    	sieve(maxn);
    	scanf("%d",&cas);
    	while(cas--){
    		scanf("%d %d",&n,&m);
    		printf("%lld
    ",calc(n,m));
    	}
    }
    
  • 相关阅读:
    c++中,map的使用
    关于C++中vector<vector<int> >的使用
    python中如何向shell脚本传递带空格的参数
    数组初始化及赋值的方法,memset的使用
    leetcode:首个缺少的最小正整数(js实现。)
    安装cmake过程出错:Error when bootstrapping CMake: Cannot find a C++ compiler that supports both C++11 and the specified C++ flags.
    React系列--三大属性 props refs state
    React系列--组件Component
    React系列--jsx语法及虚拟dom,渲染
    c++的复制构造函数,(郑莉 c++语言程序设计)
  • 原文地址:https://www.cnblogs.com/birchtree/p/11366642.html
Copyright © 2011-2022 走看看