20240920 随机训练

GYM 105348 A

题目描述

\(N\) 个团队要两两进行一次比赛,每一场比赛恰好一方胜,一方败。你可以决定每场比赛的胜负。

接着我们要对这些团队进行排名。胜场多的排在前面,胜场一样的积分率高的排在前面。积分率也由你控制。

你要让你的团队进入前四名,求你的团队的最小胜场数。

思路

我们想让自己赢得尽可能少,也就是让别人尽可能多,所以让前三名分别赢 \(N-1,N-2,N-3\) 场,然后让其余人赢得场数尽可能平均即可。

时空复杂度均为 \(O(1)\)

代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

int t, n;

void Solve() {
  cin >> n;
  ll x = 1ll * n * (n - 1) / 2 - (n - 1) - (n - 2) - (n - 3);
  cout << x / (n - 3) + (x % (n - 3) > 0) << "\n";
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  for(cin >> t; t--; Solve()) {
  }
  return 0;
}

GYM 105348 F

题目描述

给定一个长度为 \(N\) 的排列 \(A\),我们定义一个长度为 \(k\) 的数组 \(a\) 的价值如下:

  • 构造一个长度为 \(k\) 的排列 \(b\),使得元素在 \(b\) 中的相对大小与 \(a\) 相同。
  • \(a\) 的价值为 \(b_k\)

\(A\) 的所有非空字串的价值之和。

思路

我们考虑每个数对答案的贡献,一个数 \(A_i\) 会对所有 \(j\ge i,A_j\ge A_i\) 的数造成 \(i\) 的贡献,因为只要左端点 \(\le i\),那么就会使答案 \(+1\)。使用树状数组维护即可。

空间复杂度 \(O(N)\),时间复杂度 \(O(N\log N)\)

代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

const int MAXN = 1000001;

int t, n;
ll ans, tr[MAXN];

void update(int p, int x) {
  for(; p <= n; tr[p] += x, p += (p & -p)) {
  }
}

ll Getsum(int p) {
  ll sum = 0;
  for(; p; sum += tr[p], p -= (p & -p)) {
  }
  return sum;
}

void Solve() {
  cin >> n;
  ans = 0;
  for(int i = 1; i <= n; ++i) {
    tr[i] = 0;
  }
  for(int i = 1, x; i <= n; ++i) {
    cin >> x;
    update(x, i);
    ans += Getsum(x);
  }
  cout << ans << "\n";
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  for(cin >> t; t--; Solve()) {
  }
  return 0;
}

GYM 105345 I

题目描述

\(N\) 个房子,第 \(i\) 个房子将在时刻 \(A_i\) 关闭,你一开始在第 \(k\) 个房子,每一秒钟你可以走到左/右边的房子。你要在一个房子关闭前到达。如果你在房子关闭的时刻刚好到达也不行。

如果你不能造访所有的房子,输出 \(-1\),否则输出最少的消耗时间。

思路

区间 dp 即可。

时空复杂度均为 \(O(N^2)\)

代码

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 2001, INF = int(1e9);

int n, k, a[MAXN], dp[MAXN][MAXN][2];

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> k;
  for(int i = 1; i <= n; ++i) {
    cin >> a[i];
  }
  for(int i = 1; i <= n; ++i) {
    for(int j = 1; j <= n; ++j) {
      dp[i][j][0] = dp[i][j][1] = INF;
    }
  }
  dp[k][k][0] = dp[k][k][1] = 0;
  for(int len = 1; len < n; ++len) {
    for(int l = 1, r = len; r <= n; ++l, ++r) {
      if(l > 1) {
        dp[l - 1][r][0] = min({dp[l - 1][r][0], (dp[l][r][0] + 1 < a[l - 1] ? dp[l][r][0] + 1 : INF), (dp[l][r][1] + r - l + 1 < a[l - 1] ? dp[l][r][1] + r - l + 1 : INF)});
      }
      if(r < n) {
        dp[l][r + 1][1] = min({dp[l][r + 1][1], (dp[l][r][1] + 1 < a[r + 1] ? dp[l][r][1] + 1 : INF), (dp[l][r][0] + r - l + 1 < a[r + 1] ? dp[l][r][0] + r - l + 1 : INF)});
      }
    }
  }
  cout << (min(dp[1][n][0], dp[1][n][1]) == INF ? -1 : min(dp[1][n][0], dp[1][n][1]));
  return 0;
}

GYM 105345 J

题目描述

\(N\) 张牌,第 \(i\) 张牌上的数字为 \(d_i\)。有 \(Q\) 次询问:

  • 将第 \(i\) 张牌设为 \(x\)
  • 求有多少种方案从 \(d_l,d_{l+1},\dots,d_r\) 中选出一些数,使得其乘积 \(\bmod 13 =5\)

思路

我们用 \(v_i\) 表示使得乘积为 \(i\) 的方案数,并使用线段树维护。

很明显有以下转移:\(v_{2,ij\bmod 13} \leftarrow v_{0,i}\cdot v_{1,j}\)

空间复杂度 \(O(NV)\),时间复杂度 \(O(QV\log N)\)。其中 \(V=13\)

代码

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 10001, MOD = int(1e9) + 7;

struct INFO {
  int d[13];
  INFO operator+(const INFO &x) {
    INFO ret;
    for(int i = 0; i < 13; ++i) {
      ret.d[i] = (d[i] + x.d[i]) % MOD;
    }
    for(int i = 0; i < 13; ++i) {
      for(int j = 0; j < 13; ++j) {
        ret.d[i * j % 13] = (ret.d[i * j % 13] + 1ll * d[i] * x.d[j] % MOD) % MOD;
      }
    }
    return ret;
  }
  INFO operator+=(const INFO &x) {
    return *this = *this + x;
  }
};

struct Segment_Tree {
  int l[MAXN << 2], r[MAXN << 2], a[MAXN];
  INFO v[MAXN << 2];
  void build(int u, int s, int t) {
    l[u] = s, r[u] = t;
    if(s == t) {
      v[u].d[a[s]] = 1;
      return;
    }
    int mid = (s + t) >> 1;
    build(u << 1, s, mid), build((u << 1) | 1, mid + 1, t);
    v[u] = v[u << 1] + v[(u << 1) | 1];
  }
  void update(int u, int p, int x) {
    if(l[u] == r[u]) {
      for(int i = 0; i < 13; ++i) {
        v[u].d[i] = (i == x);
      }
      return;
    }
    (p <= r[u << 1] ? update(u << 1, p, x) : update((u << 1) | 1, p, x));
    v[u] = v[u << 1] + v[(u << 1) | 1];
  }
  INFO GetInfo(int u, int s, int t) {
    if(l[u] >= s && r[u] <= t) {
      return v[u];
    }
    INFO x;
    for(int i = 0; i < 13; ++i) {
      x.d[i] = 0;
    }
    if(s <= r[u << 1]) {
      x += GetInfo(u << 1, s, t);
    }
    if(t >= l[(u << 1) | 1]) {
      x += GetInfo((u << 1) | 1, s, t);
    }
    return x;
  }
}tr;

int n, q;

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> q;
  for(int i = 1; i <= n; ++i) {
    cin >> tr.a[i];
    tr.a[i] %= 13;
  }
  tr.build(1, 1, n);
  for(int i = 1, op, p, x, l, r; i <= q; ++i) {
    cin >> op;
    if(op == 1) {
      cin >> p >> x;
      tr.update(1, p, x % 13);
    }else {
      cin >> l >> r;
      cout << tr.GetInfo(1, l, r).d[5] << "\n";
    }
  }
  return 0;
}

GYM 105310 H

题目描述

给定一个 \(N\) 个结点,以 \(1\) 为根的树,每个结点都有一个美味度 \(v_i\)。有 \(Q\) 次查询,每次会令 \(v_x\leftarrow v_x +c\),并让你选择一个连通子图,使得该子图中深度最小的结点为 \(x\) 时的最大总美味度。

思路

首先考虑没有修改的情况,很明显有树上 dp:\(dp_u=v_u +\sum \limits_{v\in son_u} \max(0,dp_v)\)。这里 \(son_u\)\(u\) 的儿子。

我们考虑转化一下这个式子:\(dp_{u}=sum_u-\sum \limits_{v\in S_u}dp_v\),这里 \(S_u\) 表示 \(u\) 子树内 \(dp_v<0\)\(v\) 构成的集合,因为这些 \(dp\) 的贡献是负的,所以要减去。

这样我们就能更方便地维护这个 \(dp\)。由于这里 \(c\) 总是正的,所以每次只有可能令 \(dp_u<0\Rightarrow dp_u\ge 0\),也就是从减去变成了不减去,也就是加上,使用树状数组维护。而对每个点最多只有一次这样的变化,所以使用并查集找到祖先中第一个 \(dp<0\) 的点。

空间复杂度 \(O(N)\),时间复杂度 \(O((N+Q)\log N)\)

代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

const int MAXN = 500001;

int n, q, dfn[MAXN], tot, sz[MAXN], f[MAXN], _f[MAXN], a[MAXN];
ll sum[MAXN], tr[MAXN];
vector<int> e[MAXN];

int lowbit(int x) {
  return x & -x;
}

void update(int p, ll x) {
  for(; p <= n; tr[p] += x, p += lowbit(p)) {
  }
}

ll Getsum(int p) {
  ll sum = 0;
  for(; p; sum += tr[p], p -= lowbit(p)) {
  }
  return sum;
}

ll query(int l, int r) {
  return Getsum(r) - Getsum(l - 1);
}

void dfs(int u, int fa) {
  sum[u] = a[u], dfn[u] = ++tot, sz[u] = 1, _f[u] = fa;
  for(int v : e[u]) {
    if(v != fa) {
      dfs(v, u);
      sz[u] += sz[v];
      sum[u] += sum[v];
    }
  }
  ll x = sum[u] + query(dfn[u], dfn[u] + sz[u] - 1);
  f[u] = _f[u];
  if(x < 0 && _f[u]) {
    f[u] = u;
    update(dfn[_f[u]], -x);
  }
}

int getfa(int u) {
  return (f[u] == u ? u : f[u] = getfa(f[u]));
}

void modifly(int u, int x) {
  update(dfn[u], x), u = getfa(u);
  ll res = x;
  for(; u && sum[u] + query(dfn[u], dfn[u] + sz[u] - 1) >= 0; u = getfa(u)) {
    if(_f[u]) {
      update(dfn[_f[u]], sum[u] + query(dfn[u], dfn[u] + sz[u] - 1) - res);
      res += sum[u] + query(dfn[u], dfn[u] + sz[u] - 1) - res;
    }
    f[u] = _f[u];
  }
  if(_f[u]) {
    update(dfn[_f[u]], -res);
  }
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> q;
  for(int i = 1; i <= n; ++i) {
    cin >> a[i];
    f[i] = i;
  }
  for(int i = 2, u; i <= n; ++i) {
    cin >> u;
    e[u].emplace_back(i);
  }
  dfs(1, 0);
  for(int i = 1, u, w; i <= q; ++i) {
    cin >> u >> w;
    modifly(u, w);
    cout << sum[u] + query(dfn[u], dfn[u] + sz[u] - 1) << "\n";
  }
  return 0;
}
posted @ 2024-09-23 18:08  Yaosicheng124  阅读(4)  评论(0编辑  收藏  举报