A. 广告位招租中
Solution
首先数组内一定只要留下 a i ⩾ m a_i \geqslant m ai⩾m 的 a i a_i ai,然后开一个数组 v i s vis vis 记录一下 [ 1 , max ( a ) ] [1,\max(a)] [1,max(a)] 每个位置有多少数。
考虑枚举最大公约数 g ∈ [ m , max ( a ) ] g \in [m, \max(a)] g∈[m,max(a)],接着我们只要枚举 g g g 的倍数即可。对于 x = y ⋅ g x = y \cdot g x=y⋅g, x x x 是数组 a a a 中出现过的,我们要把这些 y y y 取 gcd \gcd gcd,看最终是否是 1 1 1,如果是说明这些数的 gcd \gcd gcd 为 g g g,否则是 g g g 的倍数。
这样的总时间复杂度是 O ( m l o g m ) O(mlogm) O(mlogm)。
C++ Code
#include <bits/stdc++.h>
using i64 = int64_t;
using u64 = uint64_t;
using f64 = double_t;
using i128 = __int128_t;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout << std::fixed << std::setprecision(12);
int n, m;
std::cin >> n >> m;
int max = 0;
std::vector<int> a;
for (int i = 0; i < n; i += 1) {
int x;
std::cin >> x;
if (x >= m) {
a.push_back(x);
max = std::max(max, x);
}
}
if (a.empty()) {
std::cout << "0 0\n";
return 0;
}
std::vector<int> vis(max + 1);
for (int x: a) {
vis[x] += 1;
}
int k = 0, ans = 0;
for (int g = m; g <= max; g += 1) {
int cnt = 0;
int d = 0;
for (int x = g, y = 1; x <= max; x += g, y += 1) {
if (vis[x]) {
cnt += vis[x];
d = std::gcd(d, y);
}
}
if (d == 1 and k < cnt) {
k = cnt;
ans = g;
}
}
std::cout << k << " " << ans << "\n";
return 0;
}
B. MEX of MEXes
Solution
首先,特判长度为 1 1 1 的排列,此时子数组只有 1 1 1,所以 b b b 中只有 2 2 2,答案为 1 1 1。
对 n > 1 n > 1 n>1 的排列 a a a,我们考虑 b b b 中是否可能出现 v ∈ [ 1 , n ] v \in [1, n] v∈[1,n]。对于一个确定的 v v v,如果 < v < v <v 的数都在 v v v 的一侧,说明可以选择 1 , 2 , ⋯ , v − 1 1, \ 2,\ \cdots, \ v - 1 1, 2, ⋯, v−1,而不选到 v v v,这样就产生了一个 m e x = v mex = v mex=v 的子数组;否则,只要想选全 1 , 2 , ⋯ , v − 1 1, \ 2,\ \cdots, \ v - 1 1, 2, ⋯, v−1,就一定会选到 v v v,导致没有任何一个子数组的 m e x mex mex 为 v v v。
因此我们只要用树状数组计算每个数左右各有多少数小于它,然后从 v = 1 v = 1 v=1 开始枚举到 n n n,看是否存在一个 v v v 无法成为 m e x mex mex。
但是特别注意,如果枚举完了 n n n 个数都满足条件,说明此时整个数组 a a a 的 m e x = n + 1 mex = n + 1 mex=n+1,所以答案应该是 n + 2 n + 2 n+2。
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)。
C++ Code
#include <bits/stdc++.h>
using i64 = int64_t;
using u64 = uint64_t;
using f64 = double_t;
using i128 = __int128_t;
template<class T>
struct BIT {
int n, down;
std::vector<T> a;
BIT(int _n = 0): down(0) {
init(_n);
}
BIT(std::vector<int> _a, int op): down(0) {
init(_a, op);
}
void init(int _n) {
n = _n;
a.assign(n, T{
});
}
void init(std::vector<int> _a, int op) {
assert(op == 0 or op == 1);
if (op == 0) {
init(_a.size());
for (int i = 0; i < _a.size(); i += 1) {
add(i, _a[i]);
}
} else {
int max = *std::max_element(_a.begin(), _a.end());
int min = *std::min_element(_a.begin(), _a.end());
init(max - min + 1);
for (int x: _a) {
add(x - min, 1);
}
down = min;
}
}
int lowbit(int x) {
return x & -x;
}
void add(int x, const T &v) {
for (int i = x + 1; i <= n; i += lowbit(i)) {
a[i - 1] = a[i - 1] + v;
}
}
void clear() {
a.assign(n, T{
});
}
T sum(int x) {
T ans{
};
for (int i = x; i > 0; i -= lowbit(i)) {
ans = ans + a[i - 1];
}
return ans;
}
T sum(int l, int r) {
return sum(r) - sum(l);
}
int kth(const T &k) {
int x = 0;
T cur{
};
for (int i = 1 << std::__lg(n); i; i /= 2) {
if (x + i <= n and cur + a[x + i - 1] <= k) {
x += i;
cur = cur + a[x - 1];
}
}
return x;
}
};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout << std::fixed << std::setprecision(12);
int n;
std::cin >> n;
if (n == 1) {
std::cout << 1 << "\n";
return 0;
}
std::vector<int> a(n), pos(n);
for (int i = 0; i < n; i += 1) {
std::cin >> a[i];
pos[--a[i]] = i;
}
BIT<int> bit(n);
std::vector<int> pre(n);
for (int i = 0; i < n; i += 1) {
pre[a[i]] = bit.sum(a[i]);
bit.add(a[i], 1);
}
bit.clear();
std::vector<int> suf(n);
for (int i = n - 1; i >= 0; i -= 1) {
suf[a[i]] = bit.sum(a[i]);
bit.add(a[i], 1);
}
for (int v = 0; v < n; v += 1) {
if (pre[v] > 0 and pre[v] < v or suf[v] > 0 and suf[v] < v) {
std::cout << v + 1 << "\n";
return 0;
}
}
std::cout << n + 2 << "\n";
return 0;
}
C. 战斗时回复
Solution
把时间拉长到两个时间最小公倍数,然后比较二者的大小即可。
C++ Code
#include <bits/stdc++.h>
using i64 = int64_t;
using u64 = uint64_t;
using f64 = double_t;
using i128 = __int128_t;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout << std::fixed << std::setprecision(12);
int T, H, t, n;
std::cin >> T >> H >> t >> n;
i64 lcm = std::lcm(1LL * T, 1LL * t);
i64 H1 = lcm / T * H;
i64 n1 = lcm / t * n;
std::cout << (H1 >= n1 ? "kirito": "hangeki");
return 0;
}
D. 小太阳的帕鲁世界1
Solution
我们直接从终点 ( n , m ) (n, m) (n,m) 逆向搜索即可,只是要特别注意,它对方向的要求是进来的,而我们是回去的,所以要反一下,比如说 g [ i ] [ j ] = D g[i][j] = D g[i][j]=D,正着走是 x = x + 1 x = x + 1 x=x+1,所以反着就是 x = x − 1 x = x - 1 x=x−1。
时间复杂度 O ( n m ) O(nm)