SpainOI XXIV

GYM 105325 B

题目描述

\(N\) 个站,站之间有 \(M\) 条单向道路。一条路径的代价为:

  • 令你经过的边权为 \(w_1,w_2,\dots,w_k\),则你的代价为 \(w_1\cdot k+w_2\cdot(k-1)+\dots+w_k\)

求你从 \(0\) 到其他点的最少代价。

思路

\(dp_{i,u}\) 表示还要走 \(i\) 条边,当前在 \(u\) 的最小代价。

很明显有以下转移:\(dp_{i,u}+i\cdot w\rightarrow dp_{i-1,v}(i\ge 1,v\in d_u)\)\(d_u\)\(u\) 的出边。

空间复杂度 \(O(N^2+M)\),时间复杂度 \(O(N(N+M))\)

代码

#include<bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;

const int MAXN = 1001, INF = int(1e9) + 1;

int T, n, m, dp[MAXN][MAXN];
vector<pii> e[MAXN];

void Solve() {
  cin >> n >> m;
  for(int i = 1; i <= n; ++i) {
    e[i].clear();
    for(int j = 0; j < n; ++j) {
      dp[j][i] = (i == 1 ? 0 : INF);
    }
  }
  for(int i = 1, u, v, w; i <= m; ++i) {
    cin >> u >> v >> w;
    u++, v++;
    e[u].emplace_back(v, w);
  }
  for(int i = n - 1; i >= 1; --i) {
    for(int u = 1; u <= n; ++u) {
      for(auto [v, w] : e[u]) {
        dp[i - 1][v] = min(dp[i - 1][v], dp[i][u] + i * w);
      }
    }
  }
  for(int i = 2; i <= n; ++i) {
    cout << (dp[0][i] == INF ? -1 : dp[0][i]) << " \n"[i == n];
  }
}

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

GYM 105325 B

题目描述

\(N\) 个骨牌,第 \(i\) 个骨牌位于位置 \(p_i\),高度为 \(h_i\)。对于 \(\forall 1\le i< N,p_i<p_{i+1}\)

当骨牌 \(i\) 倒下时,它会击倒所有 \(j>i且p_i+h_i>p_j\) 的骨牌。

请你判断当前局面属于哪种情况:

  • 推倒最左边的骨牌后,最右边的骨牌不会倒。输出 Pep
  • 推到最左边的骨牌后,最右边的骨牌会倒下。但存在一个不是最左或最右的骨牌,使得抽走它以后,推倒最左边的骨牌后,最右边的骨牌不会倒。输出 Ivet
  • 推到最左边的骨牌后,最右边的骨牌会倒下。不存在一个不是最左或最右的骨牌,使得抽走它以后,推倒最左边的骨牌后,最右边的骨牌不会倒。输出 Cesc

思路

\(r_i\) 表示 \(i\) 最远能推倒的骨牌。

我们记 \(S_i=\max \limits_{j=1}^i \{r_j\}\),就是当前最远能推倒的骨牌。如果存在一个骨牌\(i\) 使得 \(i\ge S_{i-1}\),那么很明显答案为 Pep

否则的话,最劣情况下一定会抽走那个 \(r_j\) 的最大值,所以我们再记录一个次大值判断是否合法即可。

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

代码

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

const int MAXN = 20001;

int t, n, p[MAXN], h[MAXN], r[MAXN];

void Solve() {
  cin >> n;
  for(int i = 1; i <= n; ++i) {
    cin >> p[i] >> h[i];
  }
  int Max = 1, Max2 = 0, pos = 0;
  for(int i = 1; i <= n; ++i) {
    r[i] = lower_bound(p + 1, p + n + 1, p[i] + h[i]) - p - 1;
    if(Max < i) {
      cout << "Pep\n";
      return;
    }
    Max = max(Max, r[i]);
  }
  Max = 1, Max2 = 1;
  for(int i = 1; i <= n; ++i) {
    if(i > 2 && (pos == 1 ? Max : Max2) < i) {
      cout << "Ivet\n";
      return;
    }
    if(r[i] > Max) {
      Max2 = Max, Max = r[i], pos = i;
    }else if(r[i] > Max2) {
      Max2 = r[i];
    }
  }
  cout << "Cesc\n";
}

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

GYM 105325 E

题目描述

给定一张图,其中有一个获胜顶点,接着两个玩家将轮流进行操作:

  • 在图中选择一个连通分量,删除该连通分量中编号最小的点和连接它的边。

先删除获胜顶点的玩家获胜。求哪些获胜顶点使得先手获胜。

思路

我们考虑对这张图建一个树,表示删掉树上某个点的父亲后就能删掉这个点。

我们可以把这个过程倒过来,看成从大到小加点。每次加入一个点,我们枚举它的出边,如果连接的点的编号小于当前点,那么交给后面处理。否则看对应的连通块,只要删掉了当前点,那么该连通块的最小值也可以被删掉,所以我们建立父子关系并合并连通块。这可以用并查集处理。

接着我们枚举每个点作为获胜点。如果当前点是连通块中的最小值,那么先手显然必胜。否则,两名玩家肯定不想删掉该点的父亲,因为只要删掉,那么另一位玩家就能立马删掉获胜点。所以两名玩家会先把除了该点的父亲的子树的所有点删掉,接下来进行操作的人必输,所以只有除了该点的父亲的子树的点数为奇数时,先手必胜。

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

代码

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

const int MAXN = 100001;

int T, n, m, f[MAXN], fa[MAXN], sz[MAXN];
vector<int> e[MAXN], g[MAXN];

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

void dfs(int u) {
  sz[u] = 1;
  for(int v : g[u]) {
    fa[v] = u;
    dfs(v);
    sz[u] += sz[v];
  }
}

void Solve() {
  cin >> n >> m;
  for(int i = 1; i <= n; ++i) {
    e[i].clear();
    f[i] = i;
  }
  for(int i = 0; i <= n; ++i) {
    g[i].clear();
  }
  for(int i = 1, u, v; i <= m; ++i) {
    cin >> u >> v;
    u++, v++;
    e[u].emplace_back(v);
    e[v].emplace_back(u);
  }
  for(int i = n; i >= 1; --i) {
    for(int v : e[i]) {
      if(v > i && getfa(i) != getfa(v)) {
        g[i].emplace_back(getfa(v));
        f[getfa(v)] = i;
      }
    }
  }
  for(int i = 1; i <= n; ++i) {
    if(f[i] == i) {
      f[i] = 0;
      g[0].emplace_back(i);
    }
  }
  dfs(0);
  for(int i = 1; i <= n; ++i) {
    if(!fa[i] || (n - sz[fa[i]]) % 2 == 1) {
      cout << i - 1 << " ";
    }
  }
  cout << "\n";
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  for(cin >> T; T--; Solve()) {
  }
  return 0;
}
posted @ 2024-09-26 11:58  Yaosicheng124  阅读(8)  评论(0编辑  收藏  举报