2020牛客NOIP赛前集训营-提高组(第五场)A题O(N)
三元组计数
https://ac.nowcoder.com/acm/contest/7613/A
所有点对最短路径问题(S2oj337)
标签: 线性筛,欧拉函数
对于 不为 的点对,距离显然为
否则考虑寻找过渡点。这里设 表示 的最小质因子,可以通过线性筛得到
-
只有一个过渡点(距离为 ):
那这个过渡点和两点的 都不为 ,显然 是 和 之间的最小过渡点, 所以两点距离为 当且仅当
-
有两个过渡点(距离为 ) :
这两个点的距离既不 又不是 ,所以 , 而这两个过渡点最小肯定是 和 所以两点距离为 当且仅当 .
-
有多个过渡点(距离为 ) :
两个过渡点无法连接的点,更多的过渡点一定也无法连接,因为 为可以与两个点相连且可以互相相连的最小的点。
-
无法连接:
那就是说 以内不存在过渡点和这两个点相连,且这两个点不直接相连。即 ,所以它一定是质数。
如果他是合数的话 不符合条件
对于直接相连的点, 一共有 对
对于距离为 的点,我不是很会求, 但是用总点对数减去其他三种情况就好了。
对于距离为 的点, 条件好多, 一个一个看。
考虑枚举 找 的数量.
直接判断 是否符合条件.
我们得到 , 可以预处理出这个范围内 的数量
如果要满足这个条件,满足要求的 就不会在一段连续的区间内, 但是 , 假设 那么 , 所以 不符合题意. 所以只要, 就等于 , 但是注意排除 的情况 我们只要在 时减去 的数量即可
所以我们只需要满足 ,
对于无法连接的点对, 预处理出关于质数个数的前缀和 。点对数为, 和除它以外的任何数 都是 .
Code :
#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;
const int N = 1e7 + 10;
ll len[5];
int vis[N], phi[N], pri[N], tot, p[N], nump[N];
int sum[N];
int n;
void euler(int n)
{
phi[1] = 1;
vis[1] = 1;
for (int i = 2; i <= n; ++i)
{
if (!vis[i]) pri[++tot] = i, phi[i] = i - 1, p[i] = i;
for (int j = 1; j <= tot && pri[j] * i <= n; ++j)
{
vis[i * pri[j]] = 1;
p[i * pri[j]] = pri[j];
if (i % pri[j] == 0)
{
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
phi[i * pri[j]] = phi[i] * phi[pri[j]];
}
}
}
int main()
{
scanf("%d", &n);
// 特判
if (n == 1)
{
cout << 0 << endl;
return 0;
}
euler(n);
for (int i = 2; i <= n; ++i)
{
len[1] += 1ll * phi[i];
}
len[1] = 1ll * n * (n - 1) / 2 - len[1];
for (int i = 1; i <= n; ++i)
{
sum[i] = sum[i - 1] + (vis[i] ^ 1);
}
int s = 0;
for (int i = n; i > n / 2; --i)
{
if (!vis[i]) len[0] += n - 1 - (sum[i] - sum[n / 2]);
}
len[0] += n - 1;
for (int i = 1; i <= n; ++i)
{
++nump[p[i]];
}
for (int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + nump[i];
for (int i = 2; i <= n; ++i)
{
if (2 * p[i] <= n)
{
len[3] += sum[n / 2] - sum[n / p[i]];
if (n / p[i] < p[i] && p[i] <= n / 2)
{
len[3] -= nump[p[i]];
}
}
}
len[3] /= 2;
len[2] = 1ll * n * (n - 1) / 2 - len[1] - len[3] - len[0];
cout << 1ll * len[3] * 3 + 1ll * len[2] * 2 + len[1] << endl;
return 0;
}
的数量指最小质因数等于 的数的数量。