zoukankan      html  css  js  c++  java
  • 「洛谷P3768」简单的数学题 莫比乌斯反演+杜教筛

    题目链接

    简单的数学题

    题目描述

    输入一个整数n和一个整数p,你需要求出

    [sum_{i=1}^nsum_{j=1}^n (icdot jcdot gcd(i,j)) mod p ]

    其中(gcd(a,b))表示(a)(b)的最大公约数

    输入

    一行两个整数(p,n)

    输出

    一行一个整数,为题目中所求值

    样例

    样例输入

    998244353 2000
    

    样例输出

    883968974
    

    数据范围

    (nleq 10^{10})
    (5 imes 10^8 leq p leq 1.1 imes 10^9​)
    (p​)为质数(但貌似也可以不是?又不用求逆元)

    题解

    自己想出来的题!但是连(WA)两发就是因为杜教筛写挂了……
    先不考虑取余,我们化一下题目中的式子,枚举(gcd)(警告!多公式)。

    [sum_{i=1}^nsum_{j=1}^n icdot jcdot gcd(i,j) ]

    [sum_{d=1}^{n}dsum_{i=1}^{leftlfloor frac{n}{d} ight floor}sum_{j=1}^{leftlfloor frac{n}{d} ight floor}[iperp j]icdot j cdot d^2 ]

    [sum_{d=1}^{n}d^3sum_{i=1}^{left lfloor frac{n}{d} ight floor}sum_{j=1}^{leftlfloor frac{n}{d} ight floor}[iperp j]icdot j ]

    [sum_{d=1}^{n}d^3sum_{p=1}^{left lfloor frac{n}{d} ight floor}mu(p)p^2cdot Big(frac{(1+leftlfloor frac{n}{dp} ight floor)leftlfloor frac{n}{dp} ight floor}{2}Big)^2 ]

    额,现在可以使用分块优化做到(O(n)​)了,但是这完全不能胜任数据范围,我们换个角度,设(dp=T​),枚举(T)会有什么结果?

    [sum_{T=1}^{n}Big(frac{(1+leftlfloor frac{n}{T} ight floor)leftlfloor frac{n}{T} ight floor}{2}Big)^2sum_{d|T}d^3cdot mu(frac{T}{d})(frac{T}{d})^2 ]

    [sum_{T=1}^{n}Big(frac{(1+leftlfloor frac{n}{T} ight floor)leftlfloor frac{n}{T} ight floor}{2}Big)^2 T^2sum_{d|T}dcdot mu(frac{T}{d}) ]

    现在好像反而变成(O(nlog n))(O(nsqrt{n}))了,别急,我们看看第二层的求和的意义——狄利克雷卷积,这是(Id)函数与(mu)函数的狄利克雷卷积,其值就等于(varphi)

    [sum_{T=1}^{n}Big(frac{(1+leftlfloor frac{n}{T} ight floor)leftlfloor frac{n}{T} ight floor}{2}Big)^2 T^2varphi(T) ]

    现在,我们只需要快速求出一个东西即可——(T^2varphi(T)),前面的部分可以分块优化,我们急需解决的就是这个函数(f(T)=T^2varphi(T))的前缀和(F(T))。显然,这是一个积性函数。

    杜教筛的公式:

    [sum_{i=1}^{n}(f*g)(i)=sum_{i=1}^{n}sum_{d|i}f(d)cdot g(frac{i}{d})=sum_{i=1}^{n}g(i)sum_{j=1}^{leftlfloor frac{n}{i} ight floor}f(j) ]

    于是我们需要一个函数与(f)卷起来,我们根据套路或枚举发现(T^2)项很恼人,于是尝试把这一项消掉,于是想到了(g(x)=x^2)

    [sum_{i=1}^{n}sum_{d|i}d^2varphi(d)cdot (frac{i}{d})^2=sum_{i=1}^{n}i^2sum_{j=1}^{leftlfloor frac{n}{i} ight floor}f(j) ]

    [sum_{i=1}^{n}i^2sum_{d|i}varphi(d)=sum_{i=1}^{n}i^2F(leftlfloor frac{n}{i} ight floor)​ ]

    根据公式(sum_{d|i}varphi(d)=i),继续变形

    [sum_{i=1}^{n}i^3=F(n)+sum_{i=2}^{n}i^2F(leftlfloor frac{n}{i} ight floor) ]

    [F(n)=sum_{i=1}^{n}i^3-sum_{i=2}^{n}i^2F(leftlfloor frac{n}{i} ight floor) ]

    由于(p(i)=i^3)(q(i)=i^2)的前缀和都有公式,我们可以对右边进行分块优化,就可以杜教筛了!这道题圆满解决,时间复杂度(O(n^{frac{2}{3}}))

    不过有些小细节要注意,比如模数乘(2)可能会爆(int)(n^2)可能会爆(long long),需要先取模再平方

    (Code:)

    #include <map>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    #define N 5000005
    #define ll long long
    map<ll, ll>Phi;
    ll n, mod, g[N];
    int p[N], h[N], phi[N], cnt;
    ll sqr(ll x)
    {
    	ll a = 2 * x + 1, b = x + 1, c = x;
    	if (b % 2 == 0)b /= 2;
    	else c /= 2;
    	if (a % 3 == 0)a /= 3;
    	else
    		if (b % 3 == 0)b /= 3;
    		else c /= 3;
    	a %= mod, b %= mod, c %= mod;
    	return a * b % mod * c % mod;
    }
    ll seq(ll x)
    {
    	ll a = x + 1, b = x;
    	if (a % 2 == 0)a /= 2;
    	else b /= 2;
    	a %= mod, b %= mod;
    	return a * b % mod;
    }
    ll vas(ll x)
    {
    	ll a = seq(x);
    	return a * a % mod;
    }
    ll G(ll x)
    {
    	if (x <= N - 5)
    		return g[int(x)];
    	if (Phi.find(x) != Phi.end())
    		return Phi[x];
    	ll ans = vas(x);
    	ll lst = 1;
    	for (ll i = 2; i <= x; i++)
    	{
    		i = x / (x / i);
    		ll w = (sqr(i) - sqr(lst)) % mod;
    		ans = (ans - w * G(x / i) % mod) % mod;
    		lst = i;
    	}
    	if (ans < 0)
    		ans += mod;
    	Phi.insert(make_pair(x, ans));
    	return ans;
    }
    ll Ans(ll x)
    {
    	ll ans = 0, lst = 0;
    	for (ll i = 1; i <= x; i++)
    	{
    		i = x / (x / i);
    		ll z = seq(x / i);
    		z = z * z % mod;
    		ans = (ans + z * (G(i) - G(lst)) % mod) % mod;
    		lst = i;
    	}
    	if (ans < 0)
    		ans += mod;
    	return ans;
    }
    int main()
    {
    	phi[1] = 1;
    	for (int i = 2; i <= N - 5; i++)
    	{
    		if (!h[i])
    		{
    			phi[i] = i - 1;
    			p[++cnt] = i;
    		}
    		for (int j = 1; j <= cnt; j++)
    		{
    			if (i * p[j] > N - 5)
    				break;
    			h[i * p[j]] = 1;
    			if (i % p[j] == 0)
    				phi[i * p[j]] = phi[i] * p[j];
    			else
    				phi[i * p[j]] = phi[i] * (p[j] - 1);
    		}
    	}
    	cin >> mod >> n;
    	for (int i = 1; i <= N - 5; i++)
    		g[i] = (g[i - 1] + 1ll * phi[i] * i % mod * i % mod) % mod;
    	cout << Ans(n) << '
    ';
    }
    
  • 相关阅读:
    通过HttpListener实现简单的Http服务
    WCF心跳判断服务端及客户端是否掉线并实现重连接
    NHibernate初学六之关联多对多关系
    NHibernate初学五之关联一对多关系
    EXTJS 4.2 资料 跨域的问题
    EXTJS 4.2 资料 控件之Grid 那些事
    EXTJS 3.0 资料 控件之 GridPanel属性与方法大全
    EXTJS 3.0 资料 控件之 Toolbar 两行的用法
    EXTJS 3.0 资料 控件之 combo 用法
    EXTJS 4.2 资料 控件之 Store 用法
  • 原文地址:https://www.cnblogs.com/ModestStarlight/p/8732392.html
Copyright © 2011-2022 走看看