Atcoder Grand Contest 032 简要题解

Limited Insertion

考虑最后一次操作,一定是当前序列中最后一个合法的位置(删前面会导致这个位置以后都不合法),模拟即可。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  vector<int> a(n);
  for (int i = 0; i < n; ++i) {
    cin >> a[i];
    --a[i];
  }
  vector<int> ans;
  for (int i = 0; i < n; ++i) {
    int p = -1;
    for (int j = 0; j < n - i; ++j) {
      if (a[j] == j) {
        p = j;
      }
    }
    if (p == -1) {
      cout << -1 << "\n";
      return 0;
    }
    ans.push_back(p);
    a.erase(a.begin() + p);
  }
  reverse(ans.begin(), ans.end());
  for (int i = 0; i < n; ++i) {
    cout << ans[i] + 1 << "\n";
  }
  return 0;
}

Balanced Neighbors

对于 n n n 是奇数的情况,考虑完全图去掉 i + j = n i+j=n i+j=n 的边;对于 n n n 是偶数的情况,考虑完全图去掉 i + j = n + 1 i+j=n+1 i+j=n+1 的边即可。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  vector<pair<int, int>> ans;
  for (int i = 1; i <= n; ++i) {
    for (int j = i + 1; j <= n; ++j) {
      if (i + j != n + (n % 2 == 0)) {
        ans.emplace_back(i, j);
      }
    }
  }
  cout << ans.size() << "\n";
  for (auto p : ans) {
    cout << p.first << " " << p.second << "\n";
  }
  return 0;
}

Three Circuits

首先判断图是否有欧拉回路。如果有至少一个度数大于等于 6 6 6 的点,可以把它在欧拉回路中出现的位置拆成三个环;如果有至少三个度数为 4 4 4 的点,也可以讨论一下拆成三个环;如果有两个度数为 4 4 4 的点,分为两种情况讨论,有自环的有解,没有自环的无解。其他情况均无解。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n, m;
  cin >> n >> m;
  vector<vector<int>> adj(n);
  vector<int> deg(n);
  for (int i = 0; i < m; ++i) {
    int from, to;
    cin >> from >> to;
    --from;
    --to;
    ++deg[from];
    ++deg[to];
    adj[from].push_back(to);
    adj[to].push_back(from);
  }
  for (int i = 0; i < n; ++i) {
    if (deg[i] & 1) {
      cout << "No" << "\n";
      return 0;
    }
  }
  for (int i = 0; i < n; ++i) {
    if (deg[i] >= 6) {
      cout << "Yes" << "\n";
      return 0;
    }
  }
  int cnt = 0;
  for (int i = 0; i < n; ++i) {
    if (deg[i] == 4) {
      ++cnt;
    }
  }
  if (cnt >= 3) {
    cout << "Yes" << "\n";
    return 0;
  }
  if (cnt == 2) {
    for (int i = 0; i < n; ++i) {
      if (deg[i] == 2) {
        int x = adj[i][0], y = adj[i][1];
        if (x == y) {
          cout << "Yes" << "\n";
          return 0;
        }
        for (int j = 0; j < (int) adj[x].size(); ++j) {
          if (adj[x][j] == i) {
            adj[x][j] = y;
            break;
          }
        }
        for (int j = 0; j < (int) adj[y].size(); ++j) {
          if (adj[y][j] == i) {
            adj[y][j] = x;
            break;
          }
        }
      }
    }
  }
  cout << "No" << "\n";
  return 0;
}

Rotation Sort

考虑两个操作实际上是把一个数往左或往右扔到任意位置,从小到大DP保留的数,别的数扔的方向是固定的。

#include <bits/stdc++.h>

using namespace std;

const long long inf = 1ll << 60;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n, a, b;
  cin >> n >> a >> b;
  vector<int> p(n + 1);
  for (int i = 1; i <= n; ++i) {
    cin >> p[i];
  }
  vector<int> q(n + 1);
  for (int i = 1; i <= n; ++i) {
    q[p[i]] = i;
  }
  vector<long long> dp(n + 1, inf);
  dp[0] = 0;
  for (int i = 1; i <= n; ++i) {
    vector<long long> new_dp(n + 1, inf);
    for (int j = 0; j < i; ++j) {
      if (dp[j] != inf) {
        if (q[i] > q[j]) {
          new_dp[i] = min(new_dp[i], dp[j]);
          new_dp[j] = min(new_dp[j], dp[j] + b);
        } else {
          new_dp[j] = min(new_dp[j], dp[j] + a);
        }
      }
    }
    swap(dp, new_dp);
  }
  cout << *min_element(dp.begin(), dp.end()) << "\n";
  return 0;
}

Modulo Pairing

可以用调整法证明:存在一个分界点,前面是 x + y &lt; M x+y&lt;M x+y<M 的匹配,后面是 x + y ≥ M x+y\ge M x+yM 的匹配,且两边的匹配一定是第一个匹配最后一个,第二个匹配倒数第二个,以此类推。二分这个分界点即可。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n, m;
  cin >> n >> m;
  vector<int> a(n * 2);
  for (int i = 0; i < n * 2; ++i) {
    cin >> a[i];
  }
  sort(a.begin(), a.end());
  int l = 0, r = n;
  while (l < r) {
    int mid = (l + r + 1) >> 1;
    bool red = true;
    for (int i = 0; i < mid; ++i) {
      if (a[n * 2 - mid + i] + a[n * 2 - mid - i - 1] < m) {
        red = false;
        break;
      }
    }
    if (red) {
      l = mid;
    } else {
      r = mid - 1;
    }
  }
  int ans = 0;
  for (int i = 0; i < l; ++i) {
    ans = max(ans, a[n * 2 - l + i] + a[n * 2 - l - i - 1] - m);
  }
  for (int i = 0; i < n - l; ++i) {
    ans = max(ans, a[i] + a[n * 2 - l * 2 - i - 1]);
  }
  cout << ans << "\n";
  return 0;
}

One Third

假设第一刀切在 0 0 0 位置,将其他切的位置分为三种颜色: [ 0 , 1 3 ) [0, \frac{1}{3}) [0,31) 的是红色, [ 1 3 , 2 3 ) [\frac{1}{3}, \frac{2}{3}) [31,32) 的是绿色, [ 2 3 , 1 ) [\frac{2}{3}, 1) [32,1) 的是蓝色。将它们的位置模 1 3 \frac{1}{3} 31 ,则问题等价于:在 0 0 0 处有一个红色点, 1 3 \frac{1}{3} 31 处有一个蓝色点,其他点在 [ 0 , 1 3 ) [0, \frac{1}{3}) [0,31) 内位置和颜色均匀随机,求期望最近异色点对距离。

可以用积分算出长度为 1 1 1 的线段,随机切成 n n n 段,第 k k k 短的期望长度为 1 n 2 + 1 n ( n − 1 ) + ⋯ + 1 n ( n − k + 1 ) \frac{1}{n^2}+\frac{1}{n(n-1)}+\cdots+\frac{1}{n(n-k+1)} n21+n(n1)1++n(nk+1)1 。考虑最短的一段,答案为 1 3 n 2 \frac{1}{3n^2} 3n21 ;如果最短的那一段两个点同色,那么再考虑次短的一段,需要额外加上 1 3 n ( n − 1 ) \frac{1}{3n(n-1)} 3n(n1)1 ,同时乘上两个点同色的概率 1 3 \frac{1}{3} 31 。以此类推,可以求出答案为 ∑ i = 0 n − 1 1 3 i + 1 n ( n − i ) \sum_{i=0}^{n-1} \frac{1}{3^{i+1}n(n-i)} i=0n13i+1n(ni)1

#include <bits/stdc++.h>

using namespace std;

const int md = (int) 1e9 + 7;

inline void add(int &x, int y) {
  x += y;
  if (x >= md) {
    x -= md;
  }
}

inline int mul(int x, int y) {
  return (int) ((long long) x * y % md);
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  vector<int> invs(n + 1);
  invs[0] = invs[1] = 1;
  for (int i = 2; i <= n; ++i) {
    invs[i] = mul(md - md / i, invs[md % i]);
  }
  int inv_3 = (md + 1) / 3;
  int coef = invs[n];
  int ans = 0;
  for (int i = 0; i < n; ++i) {
    coef = mul(coef, inv_3);
    add(ans, mul(coef, invs[n - i]));
  }
  cout << ans << "\n";
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值