230512 // 数论
夺,夺少?哦,85pts,小让一手。
什么?GM 居然还没讲过欧拉函数?震惊!
A. 征兵
http://222.180.160.110:1024/contest/3574/problem/1
GM 说,怕久了不打最小生成树我们给忘了。
笑话,就算退役十年我也不一定能忘了 Kruskal,就算在役十年我也不一定能记住 Prim。
就一板板题,没什么好说的。
#define int long long
namespace XSC062 {
using namespace fastIO;
const int cos = 1e4;
const int maxn = 2e4 + 5;
const int maxm = 5e4 + 5;
struct _ {
int u, v, w;
bool operator< (const _ &q) const {
return w > q.w;
}
};
_ t[maxm];
int f[maxn];
int T, n, m, r, res;
inline void Init(int n) {
for (int i = 1; i <= n; ++i)
f[i] = i;
return;
}
int find(int x) {
return x == f[x] ? x : f[x] = find(f[x]);
}
inline void merge(int x, int y) {
f[find(x)] = find(y);
return;
}
int main() {
read(T);
while (T--) {
read(n), read(m), read(r);
for (int i = 1; i <= r; ++i) {
read(t[i].u), read(t[i].v);
t[i].v += n, read(t[i].w);
++t[i].u, ++t[i].v;
}
std::sort(t + 1, t + r + 1);
Init(n += m), res = n * cos;
for (int i = 1; i <= r; ++i) {
int fx = find(t[i].u);
int fy = find(t[i].v);
if (fx == fy)
continue;
merge(t[i].u, t[i].v);
res -= t[i].w;
}
print(res, '\n');
}
return 0;
}
} // namespace XSC062
#undef int
B. 欧拉函数的值
http://222.180.160.110:1024/contest/3574/problem/2
嘶,太久没碰数学,欧拉函数是啥?小尴一个尬。
哦哦,看完样例懂了,\(n\) 以内和 \(n\) 互质的数个数对吧。好像是用筛法求的来着。现场推一下吧。
列表可以简单地发现,若 \(n={d_1}^{p_1}\times {d_2}^{p_2} \times \cdots \times {d_k}^{p_k}\),则 \(\phi(n) = (d_1-1)\times {d_1}^{p_1-1} \times (d_2-1)\times {d_2}^{p_2-1}\times \cdots \times (d_k-1)\times {d_k}^{p_k-1}\)。所以打个根号算法即可。
namespace XSC062 {
using namespace fastIO;
int n;
inline int phi(int n) {
int res = 1;
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) {
res *= i - 1;
n /= i;
while (n % i == 0)
res *= i, n /= i;
}
}
if (n > 1)
res *= n - 1;
return res;
}
int main() {
read(n);
while (n) {
print(phi(n), '\n');
read(n);
}
return 0;
}
} // namespace XSC062
C. 龙哥的问题
http://222.180.160.110:1024/contest/3574/problem/3
龙哥 /se
不难想到,根号枚举 \(n\) 的每个因数(注意不是质的),因为任何数和 \(n\) 的最大公约数都应该是 \(n\) 的因数之一,所以我们对于因数 \(p\),求得 \(1\sim n\) 中有多少个 \(i\) 和 \(n\) 的最大公约数是 \(p\) 即可。
那么这个数量怎么求呢?很明显,如果 \(\gcd\left(n,i\right)=p\),那么 \(\gcd\left(\dfrac np, \dfrac ip\right) = 1\)。
因为 \(i\le n\),所以 \(\dfrac ip\le \dfrac np\),所以满足 \(\gcd\left(\dfrac np, \dfrac ip\right) = 1\) 的 \(i\) 的数量就是 \(\phi\left(\dfrac np\right)\) 的值。
#define int long long
namespace XSC062 {
using namespace fastIO;
int n;
inline int phi(int n) {
int res = 1;
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) {
res *= i - 1;
n /= i;
while (n % i == 0)
res *= i, n /= i;
}
}
if (n > 1)
res *= n - 1;
return res;
}
inline int Solve(int n) {
int res = 0;
for (int i = 1; i * i <= n; ++i) {
if (n % i)
continue;
res += i * phi(n / i);
if (i * i == n)
continue;
int j = n / i;
res += j * phi(n / j);
}
return res;
}
int main() {
read(n);
print(Solve(n));
return 0;
}
} // namespace XSC062
#undef int
D. 可见的点(加强版)
http://222.180.160.110:1024/contest/3574/problem/4
不难发现,对于点 \((x, y)\),若 \(\gcd(x,y)\ne 1\),则其可见(注意坐标从 0 开始)。
对于每一个 \(x\),求得其 phi 值即可。注意到 \(x\) 是连续的,且我们对时间复杂度的需求是 \(\mathcal O(n)\),使用欧拉筛进行预处理即可。如果乐意可以再加个前缀和(注意到两坐标的对称性,答案为 \(2\times S(\phi(1\sim n)) - 1\))。
#define int long long
namespace XSC062 {
using namespace fastIO;
const int lim = 1e6;
const int mod = 1e9 + 7;
const int maxm = 1e6 + 5;
const int maxn = 1e6 + 5;
bool u[maxn];
int T, n, cnt, _t;
int p[maxm], phi[maxn];
inline void getPhi(int n) {
phi[1] = 1;
u[0] = u[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!u[i]) {
p[++cnt] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= cnt &&
p[j] * i <= n; ++j) {
u[p[j] * i] = 1;
phi[p[j] * i] = phi[p[j]] * phi[i];
if (i % p[j] == 0) {
phi[p[j] * i] = phi[i] * p[j];
break;
}
}
}
return;
}
int main() {
read(T);
getPhi(lim);
for (int i = 1; i <= lim; ++i)
(phi[i] += phi[i - 1]) %= mod;
while (T--) {
read(n);
print(++_t, ' ');
print(n, ' ');
print((2 * phi[n] + 1) % mod, '\n');
}
return 0;
}
} // namespace XSC062
#undef int
G. 线性筛素数
http://222.180.160.110:1024/contest/3574/problem/7
给我十年我都不一定忘得掉欧拉筛…… woc,打挂了。还好一眼就看出来了错在哪里。
namespace XSC062 {
using namespace fastIO;
const int maxm = 5e7 + 5;
const int maxn = 1e8 + 5;
int p[maxm];
bool u[maxn];
int n, m, cnt;
inline void Euler(int n) {
u[0] = u[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!u[i])
p[++cnt] = i;
for (int j = 1; j <= cnt &&
p[j] * i <= n; ++j) {
u[p[j] * i] = 1;
if (i % p[j] == 0)
break;
}
}
return;
}
int main() {
read(n), read(m);
Euler(n);
while (m--) {
read(n);
puts(u[n] ? "No" : "Yes");
}
return 0;
}
} // namespace XSC062
—— · EOF · ——
真的什么也不剩啦 😖