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) << '
    ';
    }
    
  • 相关阅读:
    POJ 2251 Dungeon Master
    POJ1321棋盘问题
    CODE[VS] 1003 电话连线
    CCF-201412-1-门禁系统
    CCF-201412-2-Z字形扫描
    为什么要用补码
    CCF-201409-1-相邻数对
    CCF-201409-2-画图
    CCF-201403-1-相反数
    CCF-201403-2-窗口
  • 原文地址:https://www.cnblogs.com/ModestStarlight/p/8732392.html
Copyright © 2011-2022 走看看