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 想知道:

\[\sum_{i = 1}^n \sum_{j = i + 1}^n \operatorname{dist}(i, j) \]

即树上点两两之间的距离和,然而这个树是会不断变换的,每一次都会有一条边的权值改变。每一次修改之后,小 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\) 中:

\[\begin {aligned} B_i&=\sum_{i=1}^{|s|} {|s| \choose i}*i \\ &= \sum_{i=1}^{|s|}\frac {|s|!} {(i-1)!*(|s|-i)!} \\ &= \sum_{i=1}^{|s|} |s|\times{|s|-1 \choose i-1} \\ &= |s|\times\sum_{i=0}^{|s|-1} {|s|-1 \choose i} \\ &=|s|\times 2^{|s|-1} \end {aligned} \]

然后就考虑如何从 \(B_i\) 算出 \(A_i\)

\[A_i=B_i-\sum_{i|j,j>i}A_j \]

然后没了

代码:

#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)\)

posted @ 2023-10-10 09:54  liruixiong0101  阅读(5)  评论(0编辑  收藏  举报