HDU6706 CCPC 2019網絡賽 huntian oy 推式子+杜教篩

CCPC 2019 網絡賽 HDU 6706 huntian oy

標籤

  • 奇奇怪怪的數論結論
  • 杜教篩

前言

簡明題意

  • 給定n,a,b,求: $$\sum_{i=1}^n\sum_{j=1}^igcd(i^a-j^a,i^b-j^b)[gcd(i,j)=1]%(10^9+7)$$

思路

  • 首先有一個結論: $$gcd(i^a-j^a,i^b-j^b)=i^{gcd(a,b)}-j^{gcd(a,b)}$$
  • 上面的結論對於i,j互質是成立的。關注這題,條件式裏就有[gcd(i,j)=1],因此咱們能夠直接替換:(因爲ab互質,因此指數直接去掉) $$\sum_{i=1}^n\sum_{j=1}^i(i-j)[gcd(i,j)=1]$$
  • (其實我比賽的時候猜出來gcd那一坨就等於i-j,而後我寫了個暴力驗證一下,發現有些數不相等,當時情急,就沒往這方面想了,好難過)
  • 推到這裏,就太簡單了。接下來咱們咱們把減法分離開: $$\sum_{i=1}^n\sum_{j=1}^ii[gcd(i,j)=1]-\sum_{i=1}^n\sum_{j=1}^ij[gcd(i,j)=1]$$
  • 而後分別求一下這兩個式子。第一個式子比較好求,重點是第二個式子。 $$\sum_{i=1}^n\sum_{j=1}^ij[gcd(i,j)=1]=\frac 12\sum_{i=1}^ni\phi(i)+\frac{[n>=1]}{2}$$

這個式子的推導過程放在個人博客《數論公式總結》裏面了網絡

  • 因此原式就等於 $$\sum_{i=1}^ni\phi(i)-\frac 12\sum_{i=1}^ni\phi(i)+\frac{[n>=1]}{2}=\frac 12\left(\sum_{i=1}^ni\phi(i)-1\right)$$
  • 好了,n<=1e9,暴力去算和式會超時。這裏杜教篩。
  • 令$f(n)=n\phi(n)$,$S(n)=\sum\limits_{i=1}^nf(i)$。咱們杜教篩,構造g: $$S(n)g(1)=\sum_{i=1}^n(f*g)(i)-\sum_{i=2}^ng(i)S(\frac ni)$$
  • 如今難點就在於怎麼找g函數。找g函數,咱們通常先把狄利克雷展開: $$f*g=\sum_{d|n}d\phi(d)*g(\frac nd)$$
  • 因此很顯然了,咱們取g=id,卷積就是: $$f*g=\sum_{d|n}n\phi(d)=n\sum_{d|n}\phi(d)$$
  • 而$\sum\limits_{d|n}\phi(d)=n$(這個至關於$\phiI=id$,用卷積很好證實),因此: $$(fg)(n)=\sum_{d|n}n\phi(d)=n^2$$
  • 因此杜教篩的式子: $$S(n)=\sum_{i=1}^ni^2-\sum_{i=2}^niS(\frac ni)$$
  • 而後就能很輕鬆地杜教篩啦~然而T了...
  • 上面的式子若是須要求積性函數的前綴和,那麼你們確定會寫一個線性篩。而這裏沒有,是否是就表明不須要預處理呢?其實仍是須要的,要預處理出$f(n)=n\phi(n)$的前綴和,才能下降杜教篩的複雜度。

注意事項

  • 注意溢出問題

總結

  • 杜教篩在分塊的那一部分有減法操做,記得那裏的減法操做不要寫-=,由於那裏的減法也要取模...

AC代碼

#include<cstdio>
#include<unordered_map>
using namespace std;

const int maxn = 5e6 + 10;
const int mod = 1e9 + 7;

int ksm(int a, int b)
{
	int ans = 1, base = a;
	while (b)
	{
		if (b & 1)
			ans = 1ll * ans * base % mod;
		b >>= 1;
		base = 1ll * base * base % mod;
	}
	return ans;
}

int inv6, inv2;

int prime[maxn], phi[maxn], pre[maxn];
bool no_prime[maxn];
int shai(int n)
{
	int cnt = 0;
	no_prime[1] = phi[1] = 1;

	for (int i = 2; i <= n; i++)
	{
		if (!no_prime[i])
			prime[++cnt] = i, phi[i] = i - 1;

		for (int j = 1; j <= cnt && i * prime[j] <= n; j++)
		{
			no_prime[prime[j] * i] = 1;
			phi[prime[j] * i] = i % prime[j] == 0 ? phi[i] * prime[j] : phi[i] * (prime[j] - 1);
			if (i % prime[j] == 0) break;
		}
	}

	for (int i = 1; i <= n; i++)
		pre[i] = (1ll * pre[i - 1] + 1ll * i * phi[i] % mod) % mod;

	return cnt;
}

unordered_map<int, int> rec;
int S(int n)
{
	if (n <= maxn - 10) return pre[n];
	if (rec[n]) return rec[n];
	long long ans = 1ll * n * (n + 1) % mod * (2 * n + 1) % mod * inv6 % mod;
	int l = 2, r = n;
	while (l <= n)
	{
		r = n / (n / l);
		ans = ((ans - 1ll * (l + r) * (r - l + 1) % mod * inv2 % mod * S(n / l) % mod) % mod + mod) % mod;
		l = r + 1;
	}
	return rec[n] = ans;
}

void solve()
{
	inv6 = ksm(6, mod - 2);
	inv2 = ksm(2, mod - 2);

	shai(maxn - 10);

	int t;
	scanf("%d", &t);
	while (t--)
	{
		int n, a, b;
		scanf("%d%d%d", &n, &a, &b);
		printf("%lld\n", 1ll * (S(n) - 1 + mod) % mod * inv2 % mod);
	}
}

int main()
{
	solve();
	return 0;
}
相關文章
相關標籤/搜索