AtCoder Beginner Contest 392(A-G)题解

A-B:略

C:可能题意比较绕,第i个答案就是穿着i这个号码(也就是Q[j] = i,这个时候j这个位置),看向的那个人的号码(也就是P[j])

代码:

void solve()
{
  int n;
  cin >> n;

  vi p(n + 1), q(n + 1), ans(n + 1);
  rep(i, 1, n) {
    cin >> p[i];
  }
  rep(i, 1, n) {
    cin >> q[i];
  }

  rep(i, 1, n) {
    ans[q[i]] = q[p[i]];
  }
  rep(i, 1, n) {
    cout << ans[i] << ' ';
  }
}

D:枚举哪两个集合[i,j]计算贡献,用map记录每个集合每个元素a个数,最后枚举i和j中间最小那个map计算贡献即可。

void solve()
{
  int n;
  cin >> n;

  vector<map<int, int>> cnt(n + 1);
  vi k(n + 1);
  rep(i, 1, n) {
    cin >> k[i];
    int x;
    rep(j, 1, k[i]) {
      cin >> x;
      cnt[i][x]++;
    }
  }

  double ans = 0;
  rep(i, 1, n) {
    rep(j, i + 1, n) {
      if (sz(cnt[i]) < sz(cnt[j])) {
        double dw = 1.0 * k[i] * k[j];
        double up = 0;
        for (auto &[x, c] : cnt[i]) {
          if (cnt[j].count(x)) {
            up += 1.0 * cnt[j][x] * c;
          }
        }
        ans = max(ans, up / dw);
      } else {
        double dw = 1.0 * k[i] * k[j];
        double up = 0;
        for (auto &[x, c] : cnt[j]) {
          if (cnt[i].count(x)) {
            up += 1.0 * cnt[i][x] * c;
          }
        }
        ans = max(ans, up / dw);
      }
    }
  }
  cout << fixed << setprecision(10) << ans << '\n';
}

E:考虑用并查集把没用到的边筛除出来,然后每次根据没用到的边再根据所在连通块合并即可,直到最后只剩下一个连通块。

void solve()
{
  int n, m;
  cin >> n >> m;

  DSU dsu(n);
  vector<pii> edges(m + 1);
  vi vec;
  rep(i, 1, m) {
    int u, v;
    cin >> u >> v;
    edges[i] = {u, v};
    if (dsu.same(u, v)) {
      vec.pb(i);
    } else {
      dsu.merge(u, v);
    }
  }

  if (dsu.getBlocks() == 1) {
    cout << 0 << '\n';
    return;
  }

  set<int> st;
  rep(i, 1, n) {
    if (i == dsu.find(i)) {
      st.insert(i);
    }
  }

  vector<tuple<int, int, int>> ans;
  for (auto &i : vec) {
    auto [u, v] = edges[i];
    int t = v;
    u = dsu.find(u);
    v = dsu.find(v);
    st.erase(u);
    int z = *st.begin();
    st.erase(z);
    dsu.merge(u, z);
    ans.pb({i, t, z});
    st.insert(dsu.find(u));
    if (sz(st) == 1) {
      break;
    }
  }

  cout << sz(ans) << '\n';
  for (auto &[i, x, y]: ans) {
    cout << i << ' ' << x << ' ' << y << '\n';
  }
}

F:因为前面的总会被后面的顶掉,所以我们直接倒置放,一定不会被顶掉,每次从头开始找到未被放数字的第a[i]个位置,这个位置就是当前数字,寻找的过程可以通过维护线段树,每次放一个数字就可以把这个位置上的sum标成1,在线段树上二分这个sum去寻找位置,复杂度nlogn。

const int N = 5e5 + 5;
struct node {
  int l, r;
  int sum;
} tr[N << 2];

void push_up(int u) {
  tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}

void build(int u, int l, int r) {
  tr[u] = {l, r, 0};
  if (l == r) {
    tr[u].sum = 1;
    return;
  }
  int mid = (l + r) >> 1;
  build(u << 1, l, mid);
  build(u << 1 | 1, mid + 1, r);
  push_up(u);
}

void modify(int u, int p) {
  if (tr[u].l == tr[u].r) {
    tr[u].sum = 0;
    return;
  }
  int mid = (tr[u].l + tr[u].r) >> 1;
  if (p <= mid) {
    modify(u << 1, p);
  } else {
    modify(u << 1 | 1, p);
  }
  push_up(u);
}

int find_pos(int u, int sum) {
  if (tr[u].l == tr[u].r) {
    return tr[u].l;
  }
  if (tr[u << 1].sum >= sum) {
    return find_pos(u << 1, sum);
  }
  return find_pos(u << 1 | 1, sum - tr[u << 1].sum);
}

void solve()
{
  int n;
  cin >> n;

  vi a(n + 1);
  vi ans(n + 1);
  rep(i, 1, n) {
    cin >> a[i];
  }
  build(1, 1, n);
  per(i, n, 1) {
    int p = find_pos(1, a[i]);
    ans[p] = i;
    modify(1, p);
  }
  rep(i, 1, n) {
    cout << ans[i] << ' ';
  }
}

G:经典FFT,考虑把值域a[i]看成多项式的x^a[i],把方案数看成系数,最后把多项式相乘系数就是每个值的方案数,由于原式子可以化成A+C=2B,因此我们枚举2B,只可能是偶数,且B一定要出现在原来的a数组中,这就可以计算到答案中去。

void solve()
{
  vl A(1e6 + 1), B(1e6 + 1), C(1e6 + 1);

  int n;
  cin >> n;
  rep(i, 1, n) {
    int x;
    cin >> x;
    A[x]++;
    B[x]++;
    C[x]++;
  }
  auto ret = atcoder::convolution_ll(A, B);
  ll ans = 0;
  REP(i, 2, 2000000, 2) {
    if (C[i >> 1]) {
      ans += ret[i] / 2;
    }
  }
  cout << ans;
}

AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值