Codeforces Round 931 (Div. 2)

写在前面

比赛地址:https://codeforces.com/contest/1934

大家好啊,因为我非常不会交互和构造,所以后面两题咕了。

以及这场为啥有两道交互两构造妈的,上场就被交互杀了这场又被交互构造双杀了,真好

A

签到。

考虑数形结合,本题等价于在数轴上有 \(n\) 个点,要求选择四个点使给定的式子代表的四段距离之和最大。

先排序,令 \(i=1, j=n, k = 2, l = n-1\) 即可。正确性显然,此时即可取到两点间距离的前四大值。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e6 + 10;
//=============================================================
int n, a[kN];
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T;
  std::cin >> T;
  while (T --) {
    std::cin >> n;
    for (int i = 1; i <= n; ++ i) std::cin >> a[i];
    std::sort(a + 1, a + n + 1);
    
    int ans = 0;
    for (int i = 1; i <= 2; ++ i) {
      for (int j = n; j >= n - 1; -- j) {
        ans += a[j] - a[i];
      }
    }
    std::cout << ans << "\n";
  }
  return 0;
}

B

贪心,数学。

大家好啊,因为我没脑子所以我直接暴力地在最烂复杂度内过掉了这题。

考虑完全背包预处理面值为 \(i(1\le i\le 3000)\) 时所需的最少硬币数量 \(f_{i}\)。一个显然的贪心是尽可能用较大的面值凑,于是考虑枚举该最大面值 \(i(1\le i\le 3000)\),答案即为:

\[\min_{1\le i\le 3000}\left(f_{i}\times \left\lfloor \dfrac{n}{i} \right\rfloor + f_{n\bmod i}\right) \]


正解同样考虑贪心地尽可能用较大的面值凑,但是对给定数字的性质进行了挖掘:

  • 1 不会使用超过 2 次:不如一个 3。
  • 3 不会使用超过 1 次:不如一个 6。
  • 6 不会使用超过 2 次:不如一个 15 一个 3。
  • 10 不会使用超过 2 次:不如两个 15。

不使用 15 且满足上述限制至多能凑出来 \(2 + 3 + 12 + 20 = 37\),除此之外的部分用 15 凑即可。

于是有两种解法,一是直接枚举使用上述四种硬币的次数后用 15 凑,二是预处理 \(n\le 37+15\) 的答案用 15 凑,都比我的恼弹背包明智。

以下是恼弹解法,其实还挺好写的捏。

//贪心,数学
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 3e3 + 10;
//=============================================================
int c[6] = {0, 1, 3, 6, 10, 15};
int need[kN];
//=============================================================
void Init() {
  for (int i = 1; i < kN; ++ i) need[i] = i; 

  for (int i = 1; i <= 5; ++ i) {
    for (int j = c[i]; j < kN; ++ j) {
      need[j] = std::min(need[j], need[j - c[i]] + 1);
    }
  }
  
}
int Solve(int n_) {
  int ret = n_;
  for (int i = 1; i < std::min(n_ + 1, kN); ++ i) {
    ret = std::min(ret, need[i] * (n_ / i) + need[n_ % i]);
  }
  return ret;
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  Init();
  int T; std::cin >> T;
  while (T --) {
    int n; std::cin >> n;
    std::cout << Solve(n) << "\n";
  }
  return 0;
}

C

交互

妈的交互。

限定四次询问,一个显然的想法是询问矩形的四个顶点,此时每次询问得到的可能有地雷的区域是一条对角线。

若只有一个地雷那简单,询问 \((1, 1)\)\((1, m)\) 得到的对角线的交点即为答案,两次询问即可;若有两个地雷则可能使得第二次询问被另一个地雷影响,使得对角线上没有地雷、对角线无交点、或对角线交点在矩形之外。

不过好在还有两次询问,若对角线有交点则询问交点,若没有交点或者询问后交点上没有地雷,则比较第一次和第二次询问答案哪个小,若第一次小则再询问 \((n, 1)\) 并与第一次询问的对角线求交点,否则询问 \((n, m)\) 并于第二次询问的对角线求交点,可以发现一定有交点且交点处一定有地雷。

求交点设坐标解方程即可。

//交互
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
int n, m;
//=============================================================
int query(int x_, int y_) {
  std::cout << "? " << x_ << " " << y_ << "\n";
  fflush(stdout);
  int x; std::cin >> x;
  return x;
}
std::pair <int, int> Judge1(int d1_, int d2_) {
  if ((d1_ - d2_ + m + 1) % 2 == 1) return std::make_pair(-1, -1);
  int y = (d1_ - d2_ + m + 1) / 2, x = d1_ - y + 2;
  if (y <= 0 || x <= 0) return std::make_pair(-1, -1);

  int ret3 = query(x, y);
  if (ret3 == 0) return std::make_pair(x, y);
  return std::make_pair(0, 0);
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  // std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> n >> m;
    int ret1 = query(1, 1), ret2 = query(1, m);
    if (ret1 == 0) {
      std::cout << "! " << 1 << " " << 1 << "\n";
      fflush(stdout);
      continue;
    } 
    if (ret2 == 0) {
      std::cout << "! " << 1 << " " << m << "\n";
      fflush(stdout);
      continue;
    }

    std::pair <int,int> r = Judge1(ret1, ret2);
    if (r.first > 0) {
      std::cout << "! " << r.first << " " << r.second << "\n";
      fflush(stdout);
      continue;
    }

    if (ret1 < ret2) {
      int ret4 = query(n, 1);
      if (ret4 == 0) {
        std::cout << "! " << n << " " << 1 << "\n";
      } else {
        int x = (ret1 - ret4 + n + 1) / 2, y = ret1 - x + 2;
        std::cout << "! " << x << " " << y << "\n";
      }
      fflush(stdout);
    } else {
      int ret4 = query(n, m);
      if (ret4 == 0) {
        std::cout << "! " << n << " " << m << "\n";
      } else {
        int x = (ret2 - ret4 + n + 1) / 2, y = x - 1 + m - ret2;
        std::cout << "! " << x << " " << y << "\n";  
      }
      fflush(stdout);
    }
  }
  return 0;
}

D1

构造,位运算

妈的构造。

\(n\) 的最高位和次高位分别为 \(i, j\)\(m\) 的最高位为 \(k\)

  • \(k=i\)\(k=j\) 时直接进行一次操作 \(y=m\) 并取 \(x=y\) 即可。
  • \(j<k<i\) 时无解。因为必然会有一次操作令 \(y\) 或者 \(x\oplus y\) 中第 \(i, k\) 位上同时为 1 导致大于 \(x\)
  • 否则有 \(k<j\),先进行操作 \(y = 2^j + (2^{j} - (x - 2^i - 2^j))\) 并取 \(x=x\oplus y\),使 \(x\) 消去第 \(j\) 位上的 1 并使之后全部变为 1,然后进行操作 \(y=m\) 并取 \(x=y\) 即可。

仅需两次操作,太劲爆了!

//构造,位运算
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
LL n, m;
std::vector <LL> ans;
//=============================================================
void Solve() {
  std::cin >> n >> m;
  LL maxn = 0, maxn2 = 0, maxm = 0;
  for (LL i = 0, j = 1; i < 63; ++ i, j <<= 1ll) {
    if (n & j) maxn2 = maxn, maxn = j;
    if (m & j) maxm = j;
  }
  if (maxn2 < maxm && maxm < maxn) return ;
  if (maxm == maxn || maxm == maxn2) {
    ans.push_back(m);
  } else {
    ans.push_back(n ^ maxn2 | (maxn2 - 1));
    ans.push_back(m);
  }

}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    ans.clear();
    Solve();
    if (ans.empty()) std::cout << -1 << "\n";
    else {
      std::cout << ans.size() << "\n" << n << " ";
      for (auto x: ans) std::cout << x << " ";
      std::cout << "\n";
    }
  }
  return 0;
}

写在最后

学到了什么:

  • A:数形结合,差的绝对值转化为数轴上的距离。
  • B:数字之间相互转化。
  • C:妈的交互
  • D:妈的构造
posted @ 2024-03-03 15:35  Luckyblock  阅读(47)  评论(0编辑  收藏  举报