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);
}
}