2023-10-02 模拟赛总结
模拟赛链接
排名:\(\text{rank 10}\)
分数:\(100+100+20+20=240\)
改边的时候没改边,我是什么废物。
T1:鸽子 / pigeon
题目描述:
求在 \(T\) 以内,既是 \(a\) 的倍数,又是 \(b\) 的倍数的数的个数。(\(1 \le a, b \le 10^9\),\(1 \le T \le 10^{18}\))
思路:
既是 \(a\) 的倍数,又是 \(b\) 的倍数的数一定是 \(\text{lcm}(a,b)\) 的倍数,在 \(T\) 以内的 \(\text{lcm}(a,b)\) 的倍数的个数就是 \(\dfrac{T}{\text{lcm}(a,b)}\)。
代码:
#include <bits/stdc++.h>
using namespace std;
long long a, b, T;
int main() {
freopen("pigeon.in", "r", stdin);
freopen("pigeon.out", "w", stdout);
cin >> a >> b >> T;
cout << T / (a * b / __gcd(a, b));
return 0;
}
时间复杂度:\(O(1)\),空间复杂度:\(O(1)\)
T2:排列 / permutation
题目描述:
有一个排列 \(p\),现在小 L 可以不断交换排列中的两个数,小 L 想让这个排列变成一个单位排列(满足所有 \(p_i=i\)),但他不会求最少的操作次数,你可以帮帮他吗?(\(1 \le n \le 100000\),\(1 \le T \le 5\))。
思路:
从大到小安排所有数,每次交换该数与他的目标位置的数,统计答案即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int kMaxN = 1e5 + 5;
int T, n, a[kMaxN], p[kMaxN], ans;
int main() {
freopen("permutation.in", "r", stdin);
freopen("permutation.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0);
for (cin >> T; T; T--, ans = 0) {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i], p[a[i]] = i;
}
for (int i = 1; i <= n; i++) {
if (p[i] != i) {
swap(a[i], a[p[i]]);
swap(p[a[i]], p[a[p[i]]]);
ans++;
}
}
cout << ans << '\n';
}
return 0;
}
T1:树 / tree
题目描述
小 L 有一棵树,每条边都有边权。定义 \(\operatorname{dist}(x, y)\) 表示 \(x\) 号点到 \(y\) 号点的唯一简单路径上的边权和(即 \(x\) 和 \(y\) 的距离)。现在小 L 想知道:
即树上点两两之间的距离和,然而这个树是会不断变换的,每一次都会有一条边的权值改变。每一次修改之后,小 L 都想知道树上点两两之间的距离和,你能帮帮他吗?(\(1 \le n, q \le 2 \times 10^5, 1 \le w_i \le 1000\))
思路:
由于每次回变换边权并且必须 \(O(1)\) 修改,但是每条边被经过的次数都没有改变,考虑预处理出每条边被经过的个数,其实就是这条边左右两个端点的左右点的个数的乘积,注意改边的时候一定要把边权改掉,否则就会痛失 \(80 pts\)。
代码:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int kMaxN = 2e5 + 5;
int n, q;
LL w[kMaxN], siz[kMaxN], cnt[kMaxN], ans;
struct E {
int v, id;
LL w;
};
vector<E> g[kMaxN];
void dfs(int u, int fa) {
siz[u] = 1;
for (E i : g[u]) {
int v = i.v;
if (v != fa) {
dfs(v, u);
siz[u] += siz[v];
}
}
}
void dfs1(int u, int fa) {
for (E i : g[u]) {
int v = i.v, id = i.id;
if (v != fa) {
dfs1(v, u);
cnt[id] = (siz[1] - siz[v]) * siz[v];
}
}
}
int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> q;
for (int i = 1, u, v; i < n; i++) {
cin >> u >> v >> w[i];
g[u].push_back({v, i, w[i]});
g[v].push_back({u, i, w[i]});
}
dfs(1, 0), dfs1(1, 0);
for (int i = 1; i < n; i++) {
ans += w[i] * cnt[i];
}
for (int id, wi; q; q--) {
cin >> id >> wi;
ans += (wi - w[id]) * cnt[id], w[id] = wi;
cout << ans << '\n';
}
return 0;
}
时间复杂度:\(O(n+q)\),空间复杂度:\(O(n)\)
T4:军队 / army
题目描述
约瑟夫统帅着一个军队,军队里有 \(n\) 个人,第 \(i\) 个人的战斗力为 \(a_i\)。约瑟夫称一些人 \(id_1 \dots id_k\) 为一个团体,当且仅当 \(\gcd(a_{id_1} \dots a_{id_k}) \gt 1\)。一个团体的战斗力为 \(k \times \gcd(a_{id_1} \dots a_{id_k})\)。
现在约瑟夫想知道他的军队里所有可能的团体战斗力只和,结果对 \(10^9 + 7\) 取模。(\(1 \le n, a_i \le 5 \times 10^5\))
思路:
考虑计算 \(A_i\) 为表示战力为 \(i\) 的所有团队大小之和,那么答案就是 \(\sum_{i=2}^{\infty}A_i\times i\),那么就是要求出 \(A_i\)。设 \(B_i\) 表示战斗力为 \(i\) 的倍数所有团体的大小之和,那么考虑战斗力为 \(i\) 的倍数的集合 \(S\) 如何统计到 \(B_i\) 中:
然后就考虑如何从 \(B_i\) 算出 \(A_i\):
然后没了
代码:
#include <bits/stdc++.h>
using namespace std;
const int kMaxN = 5e5 + 5, kM = 1e9 + 7;
int n, a[kMaxN];
long long ans, c[kMaxN], p[kMaxN], sum[kMaxN];
int main() {
freopen("army.in", "r", stdin);
freopen("army.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0);
cin >> n, p[0] = 1;
for (int i = 1; i <= n; i++) {
cin >> a[i], c[a[i]]++;
}
for (int i = 1; i < kMaxN; i++) {
p[i] = p[i - 1] * 2 % kM;
}
for (int i = 1; i < kMaxN; i++) {
for (int j = i + i; j < kMaxN; j += i) {
c[i] += c[j];
}
}
for (int i = 1; i < kMaxN; i++) {
sum[i] = !c[i] ? 0 : c[i] * p[c[i] - 1] % kM;
}
for (int i = kMaxN - 1; i; i--) {
for (int j = i + i; j < kMaxN; j += i) {
sum[i] = ((sum[i] - sum[j]) % kM + kM) % kM;
}
}
for (int i = 2; i < kMaxN; i++) {
ans = (ans + sum[i] * i) % kM;
}
cout << ans;
return 0;
}
时间复杂度:\(O(n\log n)\),空间复杂度:\(O(n)\)