26江苏省赛

B. 梦中星河

给定非负整数 \(x\),求三个非负整数 \(a,b,c\) 满足 \(a\times b+c=x\),并使得 \(\max(a,b,c)−\min(a,b,c)\) 尽可能小。输出该最小值。要求约 \(O(\sqrt x)\)

点击查看题解

最佳答案可在 \(x=k(k+1)\) 时取到,形如这样的数间隔为 \(2k\),约为 \(\sqrt x\),即枚举答案期望复杂度 \(O(\sqrt x)\)

\((k+1)^2=k^2+2k+1>k(k+1)\),也就是说取 \(a=b=\lfloor \sqrt x \rfloor\),不会错过最佳答案。

\(0\) 开始枚举答案 \(i\),令 \(a=\lfloor \sqrt x \rfloor,b=a+i,c=x-ab\),如果 \(ab>x\) 则调整为 \(a\gets a-1,b\gets b-1\),计算此时答案并记录历史最小答案。如果记录的答案小于等于 \(i\),则输出 \(i\) 即可。

官方题解证明枚举次数为 \(O(x^{1/4})\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define PII pair<ll, ll>
const ll N = 0, inf = 1e17;

void solve() {
ll x, ans = inf;
cin >> x;
ll a = sqrt(x), b = a - 1;
for (ll i = 0;; i++) {
b++;
if (a * b > x) {
a--, b--;
}
ll c = x - a * b;
ans = min(ans, max({a, b, c}) - min({a, b, c}));
if (ans <= i) {
cout << ans << endl;
return;
}
}
}

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}

A. 舞者僵尸

\(n\) 个初始僵尸,第 \(i\) 个在时刻 \(a_i\) 出现在位置 \(b_i\)。每个僵尸出现后,每秒向左右相邻整数点各召唤一个僵尸(新僵尸同理)。求最少秒数,使得数轴上整数区间 \([0,k]\) 内每个点都至少有一个僵尸。要求 \(O(n\log n)\)

点击查看题解

二分答案 二分秒数,求出每只僵尸的区间,按左端点排序,从 \(-1\) 开始往右端点合并,最后看有没有超过 \(k\) 即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define PII pair<ll, ll>

const ll inf = 1e17, N = 1e5 + 10;

ll n, k, a[N], b[N];

bool cmp(PII a, PII b) { return a.first < b.first; }

ll check(ll t) {
PII s[n + 10];
for (ll i = 1; i <= n; i++) {
if (t < a[i]) {
continue;
}
s[i].first = b[i] - (t - a[i]);
s[i].second = b[i] + (t - a[i]);
}
sort(s + 1, s + 1 + n, cmp);
ll tar = -1;
for (ll i = 1; i <= n; i++) {
if (s[i].first <= tar + 1) {
tar = max(tar, s[i].second);
}
}
if (tar >= k) {
return 1;
}
return 0;
}

void __() {
cin >> n >> k;
for (ll i = 1; i <= n; i++) {
cin >> a[i] >> b[i];
}

ll l = 0, r = 3e12 + 100, ans = inf;
while (l <= r) {
ll mid = (l + r) / 2;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
cout << ans << endl;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
__();
return 0;
}

F. 进行一个数的数

给定一个长度为 \(n\) 的数组 \(b\)(由原数组 \(a\) 对某个正整数 \(m\) 取模得到),求所有可能的原数组 \(a\)\(\text{mex}\) 值的个数。\(a\) 是非负整数数组,\(m\) 是未知正整数且 \(b_i=a_i \pmod m\)

点击查看题解

思维题 如果 \(x\) 可以是 \(\text{mex}\),那么比 \(x\) 小的非负整数都可以。所以问题转化为求最大可能 \(\text{mex}\)

最优的 \(m\) 应当取 \((\max b)+1\),这样的话类似于把 \(b\) 放入值域 \([0,m-1]\) 的桶里,答案为桶最小值乘以 \(m\),再加上最左边的最小值的下标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long

const ll inf = 1e17;

void __() {
ll n, len = -inf;
cin >> n;
map<ll, ll> b;
for (ll i = 1, a; i <= n; i++) {
cin >> a;
b[a]++;
len = max(len, a);
}
ll base = inf, mex = 0;
for (ll i = 0; i <= n; i++) {
if (b.count(i)) {
mex++;
} else {
break;
}
}
if (len >= mex) {
base = 0;
}
for (ll i = 0; i <= mex - 1; i++) {
base = min(base, b[i]);
}
ll ans = 0;
for (ll i = 0; i <= len - 1; i++) {
b[i] -= base;
if (b[i] >= 1) {
ans++;
} else {
break;
}
}
cout << ans + base * mex + 1 << endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
while (t--) {
__();
}
return 0;
}