ARC173 补

A Neq Number

定义一个数是 "Neq Number" 当且仅当这个数十进制下无相邻相同位,求第 \(k\) 个 "Neq Number"。

Solution

逐位考虑,当前位不能与上一位相同,那么就有 \(9\) 种可能的数可以填,所以 \(k\) 本质上是一个变种 \(9\) 进制数,然后可以模拟地还原。

\(\\\)

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

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

LL k;
int T, p;
vector<int> v;
int main() {
  cin.tie(0)->sync_with_stdio(0);
  for (cin >> T; T--;) {
    cin >> k;
    for (; k > 9; v.push_back((k - 1) % 9 + 1), k = (k - 1) / 9) {
    }
    v.push_back(k);
    p = -1;
    for (; !v.empty(); v.pop_back()) {
      p = v.back() - (v.back() <= p);
      cout << char('0' + p);
    }
    cout << '\n';
  }
  return 0;
}

B Make Many Triangles

给定 \(n\) 个点,每个点只能用一次,最多能组成多少个三角形。

Solution

如果不超过 \(\frac{2}{3}n\) 个点共线,则可以用两个线上点配一个线外点造三角形,答案 \(\frac{n}{3}\)。否则答案为线外点数。

\(\\\)

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 = 300 + 1;

int n;
int x[kN], y[kN];
int main() {
  cin.tie(0)->sync_with_stdio(0);
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> x[i] >> y[i];
  }
  int mx = 0;
  for (int i = 1; i <= n; i++) {
    for (int j = i + 1, cnt; j <= n; j++) {
      cnt = 0;
      for (int k = 1; k <= n; k++) {
        cnt += 1ll * (x[i] - x[j]) * (y[i] - y[k]) == 1ll * (x[i] - x[k]) * (y[i] - y[j]);
      }
      mx = max(mx, cnt);
    }
  }
  cout << min(n - mx, n / 3) << '\n';
  return 0;
}

C Not Median

给定一个 \(n\) 排列 \(p\),对于每个 \(i\in[1,n]\) 求出最短包含 \(i\) 的奇长度区间 \([l,r]\) 长度使得 \(p_i\) 不是这个区间的中位数。

Solution

结论题。

令第 \(i\) 位的答案为 \(ans_i\)

结论 1:对于两个位置 \(i, j\),有 \(\min(ans_i, ans_j) \leq |i - j|\)。因为在 \([i, j]\) 区间中只能有一个中位数,所以 \(i\)\(j\) 必然有一个的答案不能超过 \(|i-j|\)

结论 2:\(\sum ans_i\)\(O(n\log n)\) 的。将 \(n\) 个数划分为 \(n\) 组,每次相邻两两合并,由于结论 1,其中一半的数的答案不超过相邻间距,其和不超过序列长度 \(n\)。剩下一半继续合并。进行了 \(\log n\) 次操作,每次操作产生 \(n\) 的贡献,得证。

结论 3:对于一个 \(i\) 的答案区间 \([l,r]\),必然满足 \(l\in{i, i-1}\)\(r\in{i,i+1}\) 其中一个。注意到在找到答案区间之前,\(i\) 附近的数与 \(i\) 大小关系应为 \(\dots +-+-i+-+-\dots\)。找到答案时,显然剔除另一边更优,因为另一边一大一小不会改变 \(i\) 在序列中间位置。

于是枚举分讨即可。

\(\\\)

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 = 3e5 + 1;

int n, a[kN];
int main() {
  cin.tie(0)->sync_with_stdio(0);
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  for (int i = 1, l, r; i <= n; i++) {
    l = r = i;
    auto F = [&](int x, int y) { return 1ll * (a[x] - a[i]) * (a[y] - a[i]) > 0; };
    int ans = 3;
    for (; 1 <= l || r <= n; ans += 2, l -= 2, r += 2) {
      if (r + 1 <= n && i != 1 && F(r, r + 1) || 
          1 <= l - 1 && i != n && F(l - 1, l) || 
          1 <= l - 1 && r + 1 <= n && F(l - 1, r + 1) ||
          1 <= l - 2 && F(l - 2, l - 1) ||
          r + 2 <= n && F(r + 1, r + 2)) {
        break;
      }
    }
    if (1 <= l || r <= n) {
      cout << ans << ' ';
    } else {
      cout << "-1 ";
    }
  }
  return 0;
}

D Bracket Walk

给定一个 \(n\)\(m\) 边的强连通图,其边上有一个 左/右括号,求是否存在一条欧拉回路使得所经边形成一个合法括号序列。

Solution

结论题。

结论 1:一个括号序列如果左右括号数量相同,则可以通过若干次循环位移变为一个合法括号序列。因为括号序列一定存在一种匹配方式使得孤立的括号不在别的配对中间。又因为左右括号数量相同,那么将配好对的去掉,只剩下形如 ")))(((" 的东西,显然可以循环位移匹配上。

所以只要一个左右括号数量相同的环即可。

可以令左括号权值为 \(1\),右括号权值为 \(-1\),找和为 0 欧拉回路。

结论 2:一个强连通图无 0 欧拉回路,当且仅当这个图只有正环/负环。充分性:如果存在,那么将 正环/负环 从欧拉回路中剔除,显然欧拉回路仍然由环组成,而这些环的和是负/正的,矛盾了。必要性:有正有负的话,显然可以达到 “平衡”,没正没负就是 0 环。

所以找 正环/负环 就好了。

\(\\\)

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 = 4e3 + 1, kM = 8e3 + 1;

struct Edge {
  int u, v, t;
} e[kM];
int n, m;
char ch;
int d[kN];

bool F(int o) {
  fill_n(d + 1, n, 1e9), d[1] = 0;
  for (int T = 1; T < n; T++) {
    for (int i = 1; i <= m; i++) {
      d[e[i].v] = min(d[e[i].v], d[e[i].u] + e[i].t * o);
    }
  }
  for (int i = 1; i <= m; i++) {
    if (d[e[i].v] > d[e[i].u] + e[i].t * o) {
      return 1;
    }
  }
  return 0;
}
int main() {
  cin.tie(0)->sync_with_stdio(0);
  cin >> n >> m;
  for (int i = 1, u, v; i <= m; i++) {
    cin >> u >> v >> ch;
    e[i] = {u, v, (ch == '(') * 2 - 1};
  }
  cout << (F(-1) ^ F(1) ? "No" : "Yes") << '\n';
  return 0;
}

E Rearrange and Adjacent XOR

给定一个长度为 \(n\) 的序列 \(a\),你可以任意打乱这个序列,进行 \(n-1\) 次前缀 xor 后可以得到一个数,求这个数的最大值。

这题解写得太好了我完全没必要重新写

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

using namespace std;
using LL = unsigned long long;
using PII = pair<int, int>;
constexpr int kN = 100 + 1, kB = 60;

int n;
LL a[kN], t[kB];
void I(LL x) {
  for (int i = kB - 1; i >= 0; i--) {
    if (x & (1ll << i)) {
      if (!t[i]) {
        t[i] = x;
        return;
      }
      x ^= t[i];
    }
  }
}
LL Q(int x) {
  fill_n(t, kB, 0);
  for (int i = 1, p = 0; i <= n; i++) {
    if (x != i) {
      p && (I(a[i] ^ a[p]), 0);
      p = i;
    }
  }
  LL ret = 0;
  for (int i = kB - 1; i >= 0; i--) {
    (ret ^ t[i]) > ret && (ret ^= t[i]);
  }
  return ret;
}
int main() {
  cin.tie(0)->sync_with_stdio(0);
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  LL ans = 0;
  if (n % 4 == 2 && n != 2) {
    for (int i = 1; i <= n; i++) {
      ans = max(ans, Q(i));
    }
  } else {
    ans = Q(0);
  } 
  cout << ans << '\n';
  return 0;
}
posted @ 2024-07-27 09:37  Lightwhite  阅读(17)  评论(0)    收藏  举报