http://acm.hdu.edu.cn/showproblem.php?pid=5780
題意:
給定 x,n,求
假的解法:
一眼看起來,不太可做,按照常見套路,先輾轉相除一下,化簡得 (我也不會化簡,建議baidu.com)
看起來莫比烏斯反演傻逼題,先化簡一下
顯然第二個求和符號後面是一個迪利克雷卷積
(迪利克雷卷積)
複雜度
怎麼感覺會超時?不管了,說不定他有保證 在一定範圍內呢,先試試,然後就 TLE 到爆炸
真的題解:
於是看了一下網上的題解,反向枚舉公因數,然後通過歐拉函數來實現求出 n 個數中任選兩個互質的數字的方案數
(反向枚舉公因數)
(減去的一是因爲 gcd(1,1) = 1 ,被重複算了兩次)
然後考慮數論分塊,並用等比數列求和公式算次方就可以AC了(並不,還要判公比爲 1 )
莫比烏斯反演代碼:(TLE)
#include <bits/stdc++.h>
#define ll long long //T了就換int 試試
#define sc scanf
#define pr printf
using namespace std;
const int MAXN = 1e6 + 5;//用板子前先改範圍
bool check[MAXN];//值爲 false 表示素數,值爲 true 表示非素數
//int phi[MAXN];//歐拉函數表
int prime[MAXN];//連續素數表
int mu[MAXN + 10];//莫比烏斯函數
int tot;//素數的個數(從0開始
ll sub[MAXN];
const ll mod = 1e9 + 7;
ll power(ll a, ll b)
{
ll res = 1;
while (b)
{
if (b & 1)
res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll fang[MAXN];
void jzk()
{
//memset(check, false, sizeof(check));
//phi[1] = 1;
mu[1] = 1;
tot = 0;
for (int i = 2; i < MAXN; i++)
{
if (!check[i])
{
prime[tot++] = i;
//phi[i] = i - 1;
mu[i] = -1;
}
for (int j = 0; j < tot; j++)
{
if (i * prime[j] >= MAXN)
break;
check[i * prime[j]] = true;
if (i % prime[j] == 0)
{
//phi[i * prime[j]] = phi[i] * prime[j];
mu[i * prime[j]] = 0;
break;
}
else
{
//phi[i * prime[j]] = phi[i] * (prime[j] - 1);
mu[i * prime[j]] = -mu[i];
}
}
}
}
int main()
{
jzk();
int T;
sc("%d", &T);
while (T--)
{
ll x, n;
sc("%lld%lld", &x, &n);
fang[0] = 1;
for (int i = 1; i <= n; i++)
{
fang[i] = fang[i - 1] * x % mod;
sub[i] = 0;
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; i * j <= n; j++)
{
sub[i * j] = (sub[i * j] + mu[i] * fang[j]) % mod;
}
}
for (int i = 1; i <= n; i++)
sub[i] = (sub[i] + sub[i - 1]) % mod;
ll ans = 0, j;
for (int i = 1; i <= n; i = j + 1)
{
j = n / (n / i);
ans = (ans + (n / j) * (n / j) % mod * (sub[j] - sub[i - 1] + mod)) % mod;
}
ans = (ans - n * n % mod + mod) % mod;
pr("%lld\n", ans);
}
}
歐拉函數代碼:(AC)
#include <bits/stdc++.h>
#define ll long long //T了就換int 試試
#define sc scanf
#define pr printf
using namespace std;
const int MAXN = 1e6 + 5;//用板子前先改範圍
bool check[MAXN];//值爲 false 表示素數,值爲 true 表示非素數
int phi[MAXN];//歐拉函數表
int prime[MAXN];//連續素數表
int mu[MAXN + 10];//莫比烏斯函數
int tot;//素數的個數(從0開始
ll sub[MAXN];
const ll mod = 1e9 + 7;
void jzk()
{
//memset(check, false, sizeof(check));
phi[1] = 1;
mu[1] = 1;
tot = 0;
for (int i = 2; i < MAXN; i++)
{
if (!check[i])
{
prime[tot++] = i;
phi[i] = i - 1;
mu[i] = -1;
}
for (int j = 0; j < tot; j++)
{
if (i * prime[j] >= MAXN)
break;
check[i * prime[j]] = true;
if (i % prime[j] == 0)
{
phi[i * prime[j]] = phi[i] * prime[j];
mu[i * prime[j]] = 0;
break;
}
else
{
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
mu[i * prime[j]] = -mu[i];
}
}
}
for (int i = 1; i < MAXN; i++)
sub[i] = (phi[i] + sub[i - 1]) % mod;
}
ll power(ll a, ll b)
{
ll res = 1;
while (b)
{
if (b & 1)
res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll calc(ll k, ll a, ll b)
{
if (k == 1)
return b - a;
ll ans1 = (power(k, a + 1) - k + mod) * power(k - 1, mod - 2) % mod;
ll ans2 = (power(k, b + 1) - k + mod) * power(k - 1, mod - 2) % mod;
return (ans2 - ans1 + mod) % mod;
}
int main()
{
jzk();
int T;
sc("%d", &T);
while (T--)
{
ll x, n;
sc("%lld%lld", &x, &n);
ll ans = -n * n % mod + mod, j;
for (int i = 1; i <= n; i = j + 1)
{
j = n / (n / i);
ans = (ans + (2 * sub[n / i] - 1 + mod) * (calc(x, i - 1 , j) + mod)) % mod;
}
//for (ll g = 1; g <= n; g++)
//ans = (ans + fang[g] * (2LL * sub[n / g] - 1) + mod) % mod;
pr("%lld\n", ans);
}
}