E - Add and Mex
每个 \(a_i\) 只有在 \(0 \le a_i + k i \le n\) 时才会有贡献,即对于 \(i\) 只有 \(O(\frac{n}{i})\) 个操作是有效的。所以需要考虑的只有 \(O(n\log n)\) 个 \(a_i + ki\)。
借助小顶堆从小到大枚举 \(a_i + ki\) 即可。
AC代码
// Problem: E - Add and Mex
// Contest: AtCoder - AtCoder Beginner Contest 272
// URL: https://atcoder.jp/contests/abc272/tasks/abc272_e
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define CPPIO \
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#define ASSERT(x) ;
#define serialize() std::string("")
#endif
using i64 = int64_t;
using u64 = uint64_t;
void Initialize();
void SolveCase(int Case);
int main(int argc, char* argv[]) {
CPPIO;
int T = 1;
// std::cin >> T;
for (int t = 1; t <= T; ++t) {
SolveCase(t);
}
return 0;
}
void Initialize() {}
template <typename T>
using min_heap = std::priority_queue<T, std::vector<T>, std::greater<T>>;
std::mt19937 rng(time(0));
int rnd(int l, int r) {
return l + rng() % (r - l + 1);
}
void SolveCase(int Case) {
int n, m;
std::cin >> n >> m;
// n = m = 2e5;
min_heap<std::array<i64, 3>> q;
for (int i = 1; i <= n; ++i) {
int x;
std::cin >> x;
// x = rnd(-1e9, 1e9);
i64 d = std::max(1, (-x + i - 1) / i);
if (d <= m)
q.push({x + d * i, i, d});
}
std::vector<i64> ans(m + 1, 0);
while (!q.empty()) {
auto [value, index, count] = q.top();
q.pop();
// logd(value, index, count);
if (ans[count] == value) {
++ans[count];
}
if (count + 1 <= m && value + index <= n)
q.push({value + index, index, count + 1});
}
for (int i = 1; i <= m; ++i)
std::cout << ans[i] << "\n";
}
F - Two Strings
跑 \(a = s + s + t + t\) 的后缀数组,得到 \(rk\) 和 \(lcp\) 数组。
由于 \(f(s, i)\) 和 \(f(t, j)\) 的长度都还是 \(n\),所以这里算出来的 \(rk\) 是不准的,所以还需要根据 \(lcp\) 修正一下,具体就是后缀排序完之后,如果相邻的两个串 \(lcp \ge n\) 就认为这两个串是相等的。
然后根据对应第一个 \(s\) 和第一个 \(t\) 的 \(rk\) 就可以得出所有 \(f(s, i)\) 和 \(f(t, j)\) 的排名,根据这个排名就能计算出答案。
AC代码
// Problem: F - Two Strings
// Contest: AtCoder - AtCoder Beginner Contest 272
// URL: https://atcoder.jp/contests/abc272/tasks/abc272_f
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define CPPIO \
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#define ASSERT(x) ;
#define serialize() std::string("")
#endif
using i64 = int64_t;
using u64 = uint64_t;
void Initialize();
void SolveCase(int Case);
int main(int argc, char* argv[]) {
CPPIO;
int T = 1;
// std::cin >> T;
for (int t = 1; t <= T; ++t) {
SolveCase(t);
}
return 0;
}
void Initialize() {}
/**
* Build Suffix Array in O(n \log n) with binary lifting and radix sort.
*
* Idea
*
* Assume that the rank of all substrings with lenght w is known, let call it
* rank_{w}, then let rank_w(i) be the first key, and rank_w(i + w) be the
* second key. rank_{2w} can be calculated by using radix sort.
*
* Reference
*
* https://oi-wiki.org/string/sa/
*/
template <int alpha = 26, typename String>
std::tuple<std::vector<int>, std::vector<int>, std::vector<int>> SuffixArray(
const String& s) {
int n = s.size(), m = alpha;
/**
* suffix(i) means s_i...s_{n-1}
* sa[i] is start position fo the i-th smallest prefix
* rank[i] is rank of the suffix(i)
* lcp[i] means longest common prefix between suffix(sa[i]) and suffix(sa[i -
* 1])
*/
std::vector<int> sa(n), rank(n), lcp(n);
std::vector<int> count(std::max(m, n), 0), old_rank(n), temp(n);
// build sa with binary lifting and raidx sort.
for (int i = 0; i < n; ++i) {
rank[i] = s[i];
++count[rank[i]];
}
for (int i = 1; i < m; ++i) {
count[i] += count[i - 1];
}
for (int i = n - 1; i >= 0; --i) {
sa[--count[rank[i]]] = i;
}
for (int length = 1; length < n; length = length * 2) {
// second key.
int p = 0;
for (int i = n - length; i < n; ++i)
temp[p++] = i;
for (int i = 0; i < n; ++i)
if (sa[i] >= length)
temp[p++] = sa[i] - length;
// first key.
std::fill(count.begin(), count.end(), 0);
for (int i = 0; i < n; ++i)
++count[rank[temp[i]]];
for (int i = 1; i < m; ++i)
count[i] += count[i - 1];
for (int i = n - 1; i >= 0; --i) {
sa[--count[rank[temp[i]]]] = temp[i];
}
old_rank = rank;
m = 0;
rank[sa[0]] = m++;
for (int i = 1; i < n; ++i) {
if (old_rank[sa[i]] == old_rank[sa[i - 1]] &&
((sa[i] + length < n ? old_rank[sa[i] + length] : -1) ==
(sa[i - 1] + length < n ? old_rank[sa[i - 1] + length] : -1))) {
rank[sa[i]] = m - 1;
} else {
rank[sa[i]] = m++;
}
}
if (m == n)
break;
}
// longest common prefix
for (int i = 0, k = 0; i < n; ++i) {
if (rank[i] == 0)
continue;
if (k)
--k;
while (s[i + k] == s[sa[rank[i] - 1] + k])
++k;
lcp[rank[i]] = k;
}
return {sa, rank, lcp};
}
void SolveCase(int Case) {
int n;
std::cin >> n;
std::string s, t;
std::cin >> s;
std::cin >> t;
std::string a = s + s + t + t;
auto [sa, rk, lcp] = SuffixArray<256>(a);
for (int i = 1; i < 4 * n; ++i) {
if (lcp[i] >= n) {
rk[sa[i]] = rk[sa[i - 1]];
} else {
rk[sa[i]] = rk[sa[i - 1]] + 1;
}
}
std::vector<std::pair<int, int>> p;
p.reserve(2 * n);
for (int i = 0; i < n; ++i) {
p.push_back({rk[i], 0});
}
for (int i = 2 * n; i < 3 * n; ++i) {
p.push_back({rk[i], 1});
}
std::sort(p.begin(), p.end());
i64 ans = 0, count = 0;
for (auto [rank, type] : p) {
if (type == 0)
++count;
else
ans += count;
}
std::cout << ans << "\n";
}
G - Yet Another mod M
假设存在某个可行解 \(M\) , \(b_i = a_i \mod M\) ,\(b_i\) 的主元素为 \(z\) ,满足 \(a_i \mod M = z\) 的 \(a_i\) 构成的集合为 \(S\)。
假设任选两个 \(a_x\) 和 \(a_y\),则 \(a_x\) 和 \(a_y\) 同属于 \(S\) 的概率存在下界 \(\frac{1}{4}\),所以期望选择 \(4\) 次就能选中满足 \(a_x\) 和 \(a_y\) 同属于 \(S\) 的 \((x, y)\)。
然后假设某次随机到了 \(a_x\) 和 \(a_y\) ,考虑根据 \(a_x\) 和 \(a_y\) 求出 \(M\)。易得:
两式相减可得 \(M \mid (a_x - a_y)\)。
由此,随机选择 \(a_x\) 和 \(a_y\), 每次枚举 \(|a_x - a_y|\) 的因子看是不是满足条件的 \(M\)。
写了个单次的复杂度为 \(O(\sqrt{V} n\log n)\) 的算法,然后至多尝试 \(10\) 次就过题了。
AC代码
// Problem: G - Yet Another mod M
// Contest: AtCoder - AtCoder Beginner Contest 272
// URL: https://atcoder.jp/contests/abc272/tasks/abc272_g
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define CPPIO \
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#define ASSERT(x) ;
#define serialize() std::string("")
#endif
using i64 = int64_t;
using u64 = uint64_t;
void Initialize();
void SolveCase(int Case);
int main(int argc, char* argv[]) {
CPPIO;
int T = 1;
// std::cin >> T;
for (int t = 1; t <= T; ++t) {
SolveCase(t);
}
return 0;
}
void Initialize() {}
std::mt19937 rng(115514);
int rnd(int l, int r) {
return l + rng() % (r - l + 1);
}
void SolveCase(int Case) {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++i)
std::cin >> a[i];
std::vector<int> b(n);
auto check = [&](int m) {
for (int i = 0; i < n; ++i)
b[i] = a[i] % m;
std::sort(b.begin(), b.end());
int mx = 0;
for (int i = 0; i < n; ++i) {
int j = i;
while (j + 1 < n && b[j + 1] == b[i])
++j;
mx = std::max(mx, j - i + 1);
i = j;
}
return 2 * mx > n;
};
auto work = [&](int x, int y) {
int z = std::abs(a[x] - a[y]);
for (int m = 1; m * m <= z; ++m) {
if (z % m == 0) {
if (m >= 3 && check(m))
return m;
if (m * m != z && z / m >= 3 && check(z / m))
return z / m;
}
}
return -1;
};
for (int i = 0; i < 10; ++i) {
int x = rnd(0, n - 1);
int y;
do {
y = rnd(0, n - 1);
} while (x == y);
int ans = work(x, y);
if (ans != -1) {
std::cout << ans << "\n";
return;
}
}
std::cout << "-1\n";
}
Ex - Flipping Coins 2
这题基本没有人过,有点吓人。
To be solved。