zoukankan      html  css  js  c++  java
  • [BZOJ3512]DZY Loves Math IV(杜教筛)

    题面

    https://darkbzoj.tk/problem/3512

    题解

    前置知识

    本题不能上手直接反演,这是因为我这么做然后光荣GG了我们发现n不正常地小。

    考虑枚举n这一维。设(S(n,m)={sum_{j=1}^{m}}{varphi(nj)}),那么所求即为({sum_{i=1}^{n}}S(i,m))

    预处理出s,r数组,如果(n={prod_{i=1}^{k}}{p_i}^{alpha_i}),那么(s[n]={prod_{i=1}^{k}}p_i),而(r[n]={frac{n}{s[n]}})。这样,我们就可以从(varphi)函数中提出r,并使得剩下的部分便于操作:(以下r[n]简写成r,s[n]简写成s)

    [S(n,m)=r{sumlimits_{j=1}^{m}}{varphi(sj)} ]

    [=r{sumlimits_{j=1}^{m}}{varphi(frac{s}{(s,j)}*(s,j)*j)} ]

    刚才我们拆n为s和r的另一个好处得到了体现:(frac{s}{(s,j)})((s,j)、j)都互质。

    [原式=r{sumlimits_{j=1}^{m}}{varphi(frac{s}{(s,j)})}varphi((s,j)*j) ]

    [=r{sumlimits_{j=1}^{m}}{varphi(frac{s}{(s,j)})}varphi(j)(s,j) ]

    [=r{sumlimits_{j=1}^{m}}{varphi(frac{s}{(s,j)})}varphi(j){sumlimits_{d|(s,j)}}{varphi(d)} ]

    [=r{sumlimits_{j=1}^{m}}{varphi(j)}{sumlimits_{d|(s,j)}}{varphi(frac{s}{frac{(s,j)}{d}})} ]

    [=r{sumlimits_{j=1}^{m}}{varphi(j)}{sumlimits_{d|(s,j)}}varphi({frac{s}{d}}) ]

    [=r{sumlimits_{d|s}}{varphi(frac{s}{d})}{sumlimits_{j=1}^{m}}[d|j]{varphi(j)} ]

    [=r{sumlimits_{d|s}}{varphi(frac{s}{d})}{sumlimits_{j'=1}^{{lfloor}{frac{m}{d}}{ floor}}}{varphi(dj')} ]

    [=r{sumlimits_{d|s}}{varphi(frac{s}{d})}S(d,{lfloor}{frac{m}{d}}{ floor}) ]

    于是可以递归计算了。

    递归终止的条件是n=1(用杜教筛求和)或者m=0(返回0)。分析一下复杂度:杜教筛是(O(m^{frac{2}{3}}));计算函数(S(i,j))时,只要(j<m),j就一定在({lfloor}{frac{m}{i}}{ floor})的整除集合中。

    再加上计算(S(i,j))的时候需要枚举(s[i])的约数,由于(i<10^5),所以(i)最多有6个不同的质因子,所以(s[i])最多有64个约数。

    总时间复杂度为(O(m^{frac{2}{3}}+{sum_{i=1}^{n}}{sqrt{frac{m}{i}}}w(i))),其中(w(i))指的是(s[i])约数的数量。

    视各(w(i))为常数,积分近似可得(O(m^{frac{2}{3}}+overline{w}{sqrt{nm}})),其中({overline{w}}{leq}64)

    代码

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define ll long long 
    #define rg register
    
    const ll N = 1e5;
    const ll mod = 1e9 + 7;
    const ll iv2 = 5e8 + 4;
    
    namespace ModCalc{
    	inline void Inc(ll &x,ll y){
    		x += y;if(x >= mod)x -= mod;
    	}
    	
    	inline void Dec(ll &x,ll y){
    		x -= y;if(x < 0)x += mod;
    	}
    	
    	inline ll Add(ll x,ll y){
    		Inc(x,y);return x;
    	}
    	
    	inline ll Sub(ll x,ll y){
    		Dec(x,y);return x;
    	}
    }
    using namespace ModCalc;
    
    ll pn;
    ll pri[N+5],phi[N+5],sphi[N+5]; 
    bool isp[N+5];
    
    inline void Eular(){
    	phi[1] = 1; 
    	for(rg ll i = 2;i <= N;i++)isp[i] = 1;
    	for(rg ll i = 2;i <= N;i++){
    		if(isp[i])pri[++pn] = i,phi[i] = i - 1;
    		for(rg ll j = 1;i * pri[j] <= N;j++){
    			isp[i*pri[j]] = 0;
    			if(i % pri[j])phi[i*pri[j]] = phi[i] * (pri[j] - 1) % mod;
    			else{
    				phi[i*pri[j]] = phi[i] * pri[j] % mod;
    				break;
    			}
    		}
    	} 
    	for(rg ll i = 1;i <= N;i++)sphi[i] = Add(sphi[i-1],phi[i]);
    }
    
    unordered_map<ll,ll>Hash;
    
    inline ll sumphi(ll n){
    	if(n <= N)return sphi[n]; 
    	if(Hash.count(n))return Hash[n];
    	ll ans = 1ll * (n % mod) * Add(n % mod,1) % mod * iv2 % mod;
    	ll L,R = 1;
    	while(R < n){
    		L = R + 1,R = n / (n / L);
    		Dec(ans,(R - L + 1) * sumphi(n/L) % mod);
    	}
    	return Hash[n] = ans;
    }
    
    ll s[N+5],r[N+5];
    vector<ll>fac[N+5],g[N+5]; //fac[i]存储i的不同质因子,g[i]存储s[i]的约数 
    
    inline void prepro(){
    	for(rg ll i = 1;i <= pn;i++)
    		for(rg ll j = pri[i];j <= N;j += pri[i])fac[j].push_back(pri[i]);
    	for(rg ll i = 1;i <= N;i++){
    		for(rg ll mask = 0;mask < 1ll << fac[i].size();mask++){
    			ll cur = 1;
    			for(rg ll j = 0;j < fac[i].size();j++)
    				if((bool)(mask&(1ll<<j)))cur *= fac[i][j];
    			g[i].push_back(cur);
    		}
    		s[i] = g[i][g[i].size()-1];
    		r[i] = i / s[i];
    	}
    } 
    
    unordered_map<ll,ll>HashS[N+5];
    
    inline ll S(int n,int m){
    	if(n == 1)return sumphi(m);
    	if(!m)return 0;
    	if(HashS[n].count(m))return HashS[n][m];
    	ll ans = 0;
    	for(rg int i = 0;i < g[n].size();i++){
    		int d = g[n][i];
    		Inc(ans,phi[s[n]/d] * S(d,m/d) % mod);
    	}
    	ans = ans * r[n] % mod;
    	return HashS[n][m] = ans;
    }
    
    int main(){
    	Eular();
    	prepro();
    	ll ans = 0;
    	ll n,m;
    	scanf("%lld%lld",&n,&m);
    	for(rg ll i = 1;i <= n;i++)Inc(ans,S(i,m));
    	cout << ans << endl;
    	return 0;
    }
    
    
  • 相关阅读:
    全国省市县三级数据库
    多源教学数据管理系统之团队课设博客
    1.判断字符串中的字符是否Unique
    [转载]linux防火墙基础和管理设置iptables规则
    (转)Sed使用详解
    2.判断回文(Anagrams)
    【转载】关于23 种设计模式的有趣见解
    macbook M1芯片在centos8下安装k8s笔记
    Winform 学习初级 从WebForm到WinForm
    如何建立数据模型
  • 原文地址:https://www.cnblogs.com/xh092113/p/12304231.html
Copyright © 2011-2022 走看看