CF1895 ECR div.2 现场

Result

image.png

被 xlwang 狠狠地吊打。

场切:

A

portal

题意:有一条数轴,你站在原点上,宝箱、钥匙分别在 \(x, y\) 所在位置上。你可以在钥匙所在处捡起钥匙并携带着,也可以携带箱子走不超过 \(k\) 的距离。问能使宝箱和钥匙在同一位置上要走的最短距离。\(1\leq x,y\leq 100, 0\leq k\leq 100\)

Solution

直接分讨。当 \(x \geq y\) 时,直接带着钥匙到宝箱处,\(ans = x\)\(x < y - k\) 时把箱子搬到 \(x + k\) 上,再到 \(y\) 拿钥匙,折返回 \(x + k\) 开箱,\(ans = 2y - x - k\)\(x - k \geq y\) 时,直接把箱子搬到钥匙处,\(ans = y\)

\(\\\)

Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>

using namespace std;
using LL = long long;
using PII = pair<int, int>;

int T;
int x, y, k;

int main() {
  cin.tie(0)->sync_with_stdio(0);
  cin >> T;
  while (T--) {
    cin >> x >> y >> k;
    if (x < y - k) {
      cout << y + (y - (x + k)) << '\n';
    } else if (x >= y) {
      cout << x << '\n';
    } else {
      cout << y << '\n';
    }
  }
  return 0;
}

B

portal

题意:给定一个长度为 \(2n\) 的序列,你需要把他划分为两个长度为 \(n\) 的序列,第 \(i\) 个数分别表示路径上第 \(i\) 个点的 \(x, y\) 坐标。两点间距离为曼哈顿距离,路径长度为路径序列上相邻两点距离之和。\(2\leq n\leq 100\)

Solution

你发现实际上 \(x\)\(y\) 坐标是相互独立的,可以独立考虑。显然划分出的两个序列必定是升/降序的,否则变成升/降序可以使路径长度缩减重复走的部分。而这两个序列必定是将序列 \(a\) 排序后的 \([1, n]\)\([n + 1, 2n]\) 这两个区间,否则必定会多出右序列最左端到左序列最右端这部分。

我数组忘开两倍,怒吃一罚时,时间优势变作无。

\(\\\)

Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>

using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 200 + 1;

int T;
int n;
int a[kN];

int main() {
  cin.tie(0)->sync_with_stdio(0);
  cin >> T;
  while (T--) {
    cin >> n;
    for (int i = 1; i <= 2 * n; i++) {
      cin >> a[i];
    }
    sort(a + 1, a + 2 * n + 1);
    cout << a[n] - a[1] + a[2 * n] - a[n + 1] << '\n';
    for (int i = 1; i <= n; i++) {
      cout << a[i] << ' ' << a[i + n] << '\n';
    }
  }
  return 0;
}

C

portal

题意:给定 \(n\) 个数字串,可以选择两个串将其拼接,问总共有多少选法能使拼接后的串长度 \(l\) 为偶数,且区间 \([1, \frac{l}{2}]\)\([\frac{l}{2} + 1, l]\) 内数字之和相等。\(1\leq n\leq 2\times 10^5\),数字串长度不超过 \(5\)

Solution

我一开始想着能拼的长度就只有 \({i, i}, {1, 3}, {3, 5}, {1, 5}, {2, 4}\),直接暴力分讨。。。但是巨大麻烦。考虑统计各大情况。设 \(f_{0/1, l, c, s}\) 表示这个数字串拼在前/后面,以后拼成的数字串长度为 \(l\),当前数字串长度为 \(c\),这个数字串对以后拼成的数字串的前/后一半的和的贡献为 \(s\)。然后暴力统计暴力贡献就行了。

\(\\\)

Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>

using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 2e5 + 1, kL = 10 + 1, kV = 50;

int n;
vector<int> a, c;
int f[2][kL][kL][2 * kV];

int main() {
  cin.tie(0)->sync_with_stdio(0);
  cin >> n;
  for (int i = 1, w, l; i <= n; i++) {
    cin >> w;
    a.clear();
    for (int x = w; x > 0; x /= 10) {
      a.emplace_back(x % 10);
    }
    reverse(a.begin(), a.end());
    for (int j = (a.size() + 1) / 2 * 2; j <= 10; j += 2) {
      int s = 0;
      for (int k = 0; k < a.size(); k++) {
        if (k < j / 2) {
          s += a[k];
        } else {
          s -= a[k];  
        }
      }
      f[0][j][a.size()][s + kV]++;

      s = 0, c = a;
      reverse(c.begin(), c.end());
      for (int k = 0; k < c.size(); k++) {
        if (k < j / 2) {
          s += c[k];
        } else {
          s -= c[k];
        }
      }
      f[1][j][a.size()][s + kV]++;
    }
  }
  LL ans = 0;
  for (int i = 2; i <= 10; i += 2) {
    for (int j = 1; j <= max(i - 1, 5); j++) {
      for (int k = 0; k < 2 * kV; k++) {
        ans += 1ll * f[0][i][j][k] * f[1][i][i - j][k];
      }
    }
  }
  cout << ans << '\n';
  return 0;
}

D

portal

题意:给定一个长度为 \(n - 1\) 的序列 \(a\),需要你构造一个长度为 \(n\) 的序列 \(b\) 使其满足 \(b_i \oplus b_{i + 1} = a_i\),且 \(0\leq b_i \leq n-1\) 且两两不同。

Solution

首先 xor 差分是可以直接前缀 xor 回去得到的,我们可以直接通过序列 \(a\) 得到序列 \(b\) 区间 \([2, n]\) 中 xor 上 \(b_1\) 的所有数。我们可以直接枚举 \(b_1\) 判断得到的序列 \(b\) 是否合法。因为保证有解,所有只需要保证 \(\max\limits_{1\leq i\leq n} b_i \leq n - 1\) 即可。异或后最大值,01trie 板子。

不过天天狗叫 01trie 老哥好像没做出来/cf

\(\\\)

Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>

using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 2e5 + 1, kM = 30 * kN;

int n;
int a[kN];

int t[kM][2], tot;
void I(int x) {
  int p = 0;
  for (int i = 20, c; i >= 0; i--) {
    c = x >> i & 1;
    if (!t[p][c]) {
      t[p][c] = ++tot;
    }
    p = t[p][c];
  }
}
int Q(int x) {
  int p = 0, ret = 0;
  for (int i = 20, c; i >= 0; i--) {
    c = x >> i & 1;
    if (t[p][!c]) {
      p = t[p][!c];
      ret = 2 * ret + 1;
    } else {
      p = t[p][c];
      ret = 2 * ret;
    }
  }
  return ret;
}

int main() {
  cin.tie(0)->sync_with_stdio(0);
  cin >> n;
  for (int i = 2; i <= n; i++) {
    cin >> a[i];
  }
  int ans = 0;
  for (int i = 1; i <= n; i++) {
    a[i] ^= a[i - 1], I(a[i]);
  }
  for (int i = 0; i < n; i++) {
    if (Q(i) == n - 1) {
      for (int j = 1; j <= n; j++) {
        cout << (a[j] ^ i) << ' ';
      }
      cout << '\n';
      return 0;
    }
  }
  return 0;
}

补题

E

portal

题意:小 M 和小 B 分别有 \(n\)\(m\) 张卡牌,有相应的攻击值和防御值。一开始小 M 选出自己的一张牌打出,下一个人需要给出 攻击值比对方打出的牌的防御值严格更大的卡牌,谁无法按此规则出牌就判作那方战败,持续 \(10^{1000}\) 步就平局。问小 M 分别有多少张牌放在最开始给出可以使得最终自己 获胜/平局/战败。\(1\leq n, m\leq 3\times 10^5\)

Solution

你发现不管是谁,最优策略总是选出攻击值比对方防御值严格更大,且防御值尽量大的卡牌。于是你对于一张牌,对方将会出什么样的牌是可确定的。考虑并查集上将当前牌并向对方会出的牌,一张牌当前连通块的根节点就是你出这张牌最终会以什么牌结束。可以直接判断出这张牌是谁的牌来预测胜负。时间复杂度 \(O((n+m)\log n)\),由于无法按秩合并,无法将复杂度优化到反阿克曼级别。

\(\\\)

Code
// stODDDDDDDDDDDDDDDDDDDDDDDDDDD hzt CCCCCCCCCCCCCCCCCCCCCCCCCOrz

#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>

using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 3e5 + 2;
#define y first
#define x second

int T;
int n, m;
PII a[kN], b[kN];
int ans[2 * kN], v[2 * kN], fa[2 * kN];

int R(int x) {
  if (x == fa[x]) {
    return x;
  }
  fa[x] = R(fa[x]);
  return fa[x];
}
void M(int u, int v) {
  u = R(u), v = R(v);
  if (u == v) {
    return;
  }
  fa[u] = v;
}

int main() {
  cin.tie(0)->sync_with_stdio(0);
  cin >> T;
  while (T--) {
    cin >> n;
    for (int i = 1; i <= n; i++) {
      cin >> a[i].x;
    }
    for (int i = 1; i <= n; i++) {
      cin >> a[i].y;
    }
    cin >> m;
    for (int i = 1; i <= m; i++) {
      cin >> b[i].x;
    }
    for (int i = 1; i <= m; i++) {
      cin >> b[i].y;
    }
    sort(a + 1, a + n + 1);
    sort(b + 1, b + m + 1);

    fill_n(v + 1, n + m, 0);
    fill_n(ans + 1, n + m, 0);
    iota(fa, fa + n + m + 1, 0);
    for (int i = 1, j = m; i <= n; i++) {
      for (; j && b[j].x <= a[i].y; j--) {
      }
      if (j != 0) {
        v[i] = j + n;
        M(i, j + n);
      } else {
        break;
      }
    }
    for (int i = 1, j = n; i <= m; i++) {
      for (; j && a[j].x <= b[i].y; j--) {
      }
      if (j != 0) {
        v[i + n] = j;
        M(i + n, j);
      } else {
        break;
      }
    }
    for (int i = 1; i <= n + m; i++) {
      if (v[i]) {
        continue;
      }
      if (i <= n) {
        ans[R(i)] = -1;
      } else {
        ans[R(i)] = 1;
      }
    }
    int ans1 = 0, ans2 = 0, ans3 = 0;
    for (int i = 1; i <= n; i++) {
      int f = R(i);
      if (ans[f] == -1) {
        ans1++;
      } else if (ans[f] == 0) {
        ans2++;
      } else {
        ans3++;
      }
    }
    cout << ans1 << ' ' << ans2 << ' ' << ans3 << '\n';
  }
  return 0;
}

F

portal

题意:求存在多少个长度为 \(n\) 的序列 \(a\) 使相邻两数差不超过 \(k\),且数列中存在至少一个数在区间 \([x, x + k - 1]\) 内。\(1\leq n, k\leq 10^9, 0\leq x\leq 40\)

Solution

注意到假设我们确定了差分数组和最小值,就可以得到整个序列的具体值。这是显然的,因为差分数组可以前缀和回去得到所有元素和 \(a_1\) 的差,又知道最小值就可以推出全部。同时,两个序列如果差分序列或最小值不同,两者显然不同。

于是我们可以试求 \(\min\limits_{1\leq i\leq n}a_i \leq x+k-1\)\(|a_i-a_{i+1}|\) 的不同序列个数。发现存在 \((2k+1)^{n-1}\) 种不同的差分序列和 \(x+k\) 种最小值满足条件。只需要再除去 \(\max\limits_{1\leq i\leq n}a_i<x\) 的方案数即可。

发现这个东西很可 dp。由于序列中数的值域仅为 \([0, x]\),因此直接设 \(f_{i, j}\) 表示当前在第 \(i\) 位,且 \(a_i = j\) 的方案数量,暴力转移实在不可接受。但是由于相邻两数差不超过 \(k\) 是当前唯一的限制,直接矩阵快速幂优化即可。时间复杂度 \(O(x^3\log n)\)

\(\\\)

Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>

using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kX = 40 + 1, kP = 1e9 + 7;

int T;
int n, x, k;

struct Mat {
  int a[kX][kX];
  Mat() { fill(a[0], a[x] + x + 1, 0); }
  Mat operator*(const Mat &o) const {
    Mat ret;
    for (int i = 0; i < x; i++) {
      for (int k = 0; k < x; k++) {
        for (int j = 0; j < x; j++) {
          ret.a[i][j] = (ret.a[i][j] + 1ll * a[i][k] * o.a[k][j]) % kP;
        }
      }
    }
    return ret;
  }
} a, ret;
int P(int a, int b) {
  int ret = 1;
  for (; b > 0; b /= 2) {
    if (b & 1) {
      ret = 1ll * ret * a % kP;
    }
    a = 1ll * a * a % kP;
  }
  return ret;
}

int main() {
  cin.tie(0)->sync_with_stdio(0);
  cin >> T;
  while (T--) {
    cin >> n >> x >> k;
    int ans = 1ll * P(2 * k + 1, n - 1) * (x + k) % kP;
    for (int i = 0; i < x; i++) {
      for (int j = 0; j < x; j++) {
        a.a[i][j] = (abs(i - j) <= k);
      }
    }
    ret = Mat();
    int b = n - 1;
    for (int i = 0; i < x; i++) {
      ret.a[0][i] = 1;
    }
    for (; b > 0; b /= 2) {
      if (b & 1) {
        ret = ret * a;
      }
      a = a * a;
    }
    for (int i = 0; i < x; i++) {
      ans = (ans - ret.a[0][i] + kP) % kP;
    }
    cout << ans << '\n';
  }
  return 0;
}
posted @ 2023-11-04 01:33  Lightwhite  阅读(27)  评论(0)    收藏  举报