题解 P6810 【「MCOI-02」Convex Hull 凸包】
一句话题意:
求出
前置知识
-
这里我们只需要了解他的一个性质, $\tau * \mu = \epsilon$, 具体证明如下: > $\tau = 1 * 1$, > $\epsilon = \mu * 1 对于第一个柿子,两边同时卷上一个
\mu 变成:\tau * \mu = 1 * 1 * \mu \tau * \mu = 1 * \epsilon = 1 2.线性筛约数个数
首先,我们对一个数有唯一分解定理:
那么这个数的约数个数可以写为 (这小学知识啦)。
我们维护两个数组
对于 质数
对于不是质数的数
若
则有
若
Code:
g[1] = 1; t[1] = 1;
for(int i = 2; i <= N-5; i++)
{
if(!check[i])
{
prime[++tot] = i;
g[i] = 1;
t[i] = 2;
}
for(int j = 1; j <= tot && i * prime[j] <= N-5; j++)
{
check[i * prime[j]] = 1;
if(i % prime[j] == 0)
{
g[i * prime[j]] = g[i] + 1;
t[i * prime[j]] = t[i] * (g[i * prime[j]] + 1) / (g[i] + 1);
break;
}
else
{
g[i * prime[j]] = 1;
t[i * prime[j]] = t[i] * t[prime[j]];
}
}
}
题解
比较难的懵逼乌斯反演题。
首先,我们还是按照套路先枚举一个
后面那两个求和柿子可以提出一个
根据莫比乌斯反演,我们可以得到一个等式:
把这个等式代回原式有:
先枚举一下
后面的那个求和柿子可以变成:
设
改变一下枚举顺序,先枚举一下
中间的这个
我们的柿子就可以变为:
这个直接枚举的复杂度跟定会爆的,我们还要考虑优化。
我们可以对于每个
据说还可以用
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 2e6+10;
int n,m,p,tot;
int t[N],g[N],prime[N],sum1[N],sum2[N];
bool check[N];
inline int read()
{
int s = 0,w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
return s * w;
}
void YYCH()
{
g[1] = 1; t[1] = 1;
for(int i = 2; i <= N-5; i++)//预处理出每个数的约数个数
{
if(!check[i])
{
prime[++tot] = i;
g[i] = 1;
t[i] = 2;
}
for(int j = 1; j <= tot && i * prime[j] <= N-5; j++)
{
check[i * prime[j]] = 1;
if(i % prime[j] == 0)
{
g[i * prime[j]] = g[i] + 1;
t[i * prime[j]] = t[i] * (g[i * prime[j]] + 1) / (g[i] + 1);
break;
}
else
{
g[i * prime[j]] = 1;
t[i * prime[j]] = t[i] * t[prime[j]];
}
}
}
}
int slove(int n,int m)
{
YYCH();
int res = 0;
for(int d = 1; d <= n; d++)//预处理出每个 Q 的贡献
{
for(int j = 1; j <= n/d; j++)
{
sum1[d] = (sum1[d] + t[j * d]) % p;
}
}
for(int d = 1; d <= m; d++)
{
for(int j = 1; j <= m/d; j++)
{
sum2[d] = (sum2[d] + t[j * d]) % p;
}
}
for(int d = 1; d <= n; d++)//枚举每个Q
{
res = (res + 1LL * sum1[d] * sum2[d] % p) % p;//计算答案
}
return res % p;
}
int main()
{
n = read(); m = read(); p = read();
if(n > m) swap(n,m);
printf("%d\n",slove(n,m));
return 0;
}
话说,这题感觉难度有点低,应该是紫题吧,毕竟最后的