A.Destroying Bridges(思维)
题意:
有 n n n个岛屿,编号为 1 , 2 , … , n 1,2,\ldots,n 1,2,…,n。最初,每对岛屿都由一座桥连接。因此,一共有 n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n−1)座桥。
Everule住在 1 1 1号岛上,喜欢利用桥梁访问其他岛屿。Dominater有能力摧毁最多 k k k座桥梁,以尽量减少Everule可以使用(可能是多座)桥梁到达的岛屿数量。
如果Dominater以最佳方式摧毁桥梁,求Everule可以访问的岛屿(包括岛屿 1 1 1)的最少数量。
分析:
本题岛屿和桥构成一个完全图,每个点的连边数为 n − 1 n-1 n−1,要么至少摧毁 1 1 1号岛连接其他 n − 1 n-1 n−1座岛的所有 n − 1 n-1 n−1座桥,这样只能访问 1 1 1岛,要么所有岛都能访问。
代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int n, k;
cin >> n >> k;
if (k >= n - 1)
cout << 1 << endl;
else
cout << n << endl;
}
return 0;
}
B.Equal XOR(数学)
题意:
给你一个长度为 2 n 2n 2n的数组 a a a,其中从 1 1 1到 n n n的每个整数都出现两次。
同时给你一个整数 k k k( 1 ≤ k ≤ ⌊ n 2 ⌋ 1\leq k\leq\lfloor\frac{n}{2}\rfloor 1≤k≤⌊2n⌋)。
你需要找到两个长度分别为 2 k \mathbf{2k} 2k的数组 l l l和 r r r,使得:
- l l l是 [ a 1 , a 2 , … a n ] [a_1,a_2,\ldots a_n] [a1,a2,…an]的子集 † ^\dagger †。
- r r r是 [ a n + 1 , a n + 2 , … a 2 n ] [a_{n+1},a_{n+2},\ldots a_{2n}] [an+1,an+2,…a2n]的子集
- l l l中元素的按位异或等于 r r r中元素的按位异或;换句话说, l 1 ⊕ l 2 ⊕ … ⊕ l 2 k = r 1 ⊕ r 2 ⊕ … ⊕ r 2 k l_1\oplus l_2\oplus\ldots\oplus l_{2k}=r_1\oplus r_2\oplus\ldots\oplus r_{2k} l1⊕l2⊕…⊕l2k=r1⊕r2⊕…⊕r2k
可以证明至少有一对 l l l和 r r r总是存在的。如果有多个解,可以输出其中任意一个。
† ^\dagger † 序列 x x x是序列 y y y的子集,如果 x x x可以通过删除 y y y中的几个元素(可能一个元素也没有或全部元素)并按任意顺序重新排列而得到。例如, [ 3 , 1 , 2 , 1 ] [3,1,2,1] [3,1,2,1]、 [ 1 , 2 , 3 ] [1,2,3] [1,2,3]、 [ 1 , 1 ] [1,1] [1,1]和 [ 3 , 2 ] [3,2] [3,2]是 [ 1 , 1 , 2 , 3 ] [1,1,2,3] [1,1,2,3]的子集,但 [ 4 ] [4] [4]和 [ 2 , 2 ] [2,2] [2,2]不是 [ 1 , 1 , 2 , 3 ] [1,1,2,3] [1,1,2,3]的子集。
分析:
对于每个数字有三种情况:
- 只出现在 ( 1 , n ) (1,n) (1,n)
- 只出现在 ( n + 1 , 2 n ) (n+1,2n) (n+1,2n)
- 一个出现在 ( 1 , n ) (1,n) (1,n),另一个出现在 ( n + 1 , 2 n ) (n+1,2n) (n+1,2n)
前两种情况数目是相同的。我们先用前两种成对分别填入 l , r l,r l,r中,不够的再用第三种补即可。
代码:
#include<bits/stdc++.h>
using namespace std;
void solve() {
int n, k;
cin >> n >> k;
vector<int> a(2 * n + 2);
for (int i = 1; i <= 2 * n; i++) {
cin >> a[i];
}
vector<int> l, r;
vector<int> vis(2 * n + 5);
for (int i = 1; i <= n; i++) {
vis[a[i]] += 1;
}
for (int i = n + 1; i <= 2 * n; i++) {
vis[a[i]] += 2;
}
vector<int> ls, rs, t;
for (int i = 1; i <= n; i++) {
if (vis[a[i]] == 3) {
t.push_back(a[i]);
} else if (vis[a[i]] == 2) {
ls.push_back(a[i]);
}
}
for (int i = n + 1; i <= 2 * n; i++) {
if (vis[a[i]] == 4) {
rs.push_back(a[i]);
}
}
sort(ls.begin(), ls.end());
ls.erase(unique(ls.begin(), ls.end()), ls.end());
sort(rs.begin(), rs.end());
rs.erase(unique(rs.begin(), rs.end()), rs.end());
for (int i = 0; i < min(ls.size(), rs.size()) && l.size() < 2 * k; i++) {
l.push_back(ls[i]);
l.push_back(ls[i]);
r.push_back(rs[i]);
r.push_back(rs[i]);
}
for (int i = 0; i < t.size() && l.size() < 2 * k; i++) {
l.push_back(t[i]);
r.push_back(t[i]);
}
for (auto x: l) {
cout << x << " ";
}
cout << endl;
for (auto x: r) {
cout << x << " ";
}
cout << endl;
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
C.MEX Game 1(思维)(DIV1-A)
题意:
爱丽丝和鲍勃在大小为 n n n的数组 a a a上进行另一场博弈。爱丽丝从一个空数组 c c c开始。双方轮流下棋,爱丽丝先开始。
轮到爱丽丝时,她从 a a a中选取一个元素,将其追加到 c c c中,然后从 a a a中删除。
轮到鲍勃时,他从 a a a中选取一个元素,然后从 a a a中删除。
当数组 a a a为空时,游戏结束。游戏的分数定义为 c c c的MEX † ^\dagger † 。爱丽丝希望最大化得分,而鲍勃希望最小化得分。如果双方都以最佳状态进行游戏,求游戏的最终得分。
† ^\dagger †整数数组的 MEX \operatorname{MEX} MEX(最小不等式)定义为数组中不出现的最小非负整数。例如
- [ 2 , 2 , 1 ] [2,2,1] [2,2,1]的 MEX \operatorname{MEX} MEX是 0 0 0,因为 0 0 0不属于数组。
- [ 3 , 1 , 0 , 1 ] [3,1,0,1] [3,1,0,1]的 MEX \operatorname{MEX} MEX是 2 2 2,因为 0 0 0和 1 1 1属于数组,而 2 2 2不属于数组。
- [ 0 , 3 , 1 , 2 ] [0,3,1,2] [0,3,1,2]的 MEX \operatorname{MEX} MEX是 4 4 4,因为 0 0 0、 1 1 1、 2 2 2和 3 3 3属于数组,而 4 4 4不属于数组。
分析:
出现次数大于 1 1 1次的,无论先后手爱丽丝肯定都能抢到。
由于先手的缘故,爱丽丝可以保证将一个出现次数为 1 1 1的数拿到手,显然开局拿最小的且出现次数为 1 1 1的数是最优的,所以我们按照 MEX \operatorname{MEX} MEX,从小到大优先拿出现次数为 1 1 1的数,然后从小到大找第一个拿不到的数即可。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
void solve() {
int n;
cin >> n;
vector<int> a(n + 5);
map<int, int> tmp;
vector<bool> vis(n + 5);
for (int i = 1; i <= n; i++) {
cin >> a[i];
tmp[a[i]]++;
}
for (auto t: tmp) {
if (t.second >= 2) {
vis[t.first] = true;
}
}
int cur = 0;
while (vis[cur]) {
cur++;
}
for (int i = 1; i <= n; i++) {
while (vis[cur]) {
cur++;
}
if (tmp[cur] == 1) {
tmp[cur]--;
vis[cur] = true;
} else {
break;
}
while (vis[cur]) {
cur++;
}
if (tmp[cur] == 1) {
tmp[cur]--;
}
}
LL ans = 0;
while (vis[ans]) {
ans++;
}
cout << ans << endl;
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
D.Non-Palindromic Substring(思维、贪心)(DIV1-B)
题意:
如果至少存在一个长度为 k k k的子串 † ^\dagger †不是回文 ‡ ^\ddagger ‡,则称字符串 t t t为好 k k k字符串。令 f ( t ) f(t) f(t)表示所有 k k k的值之和,使得字符串 t t t是好 k k k字符串。
给你一个长度为 n n n的字符串 s s s。回答 q q q个问题:
- 给定 l l l和 r r r( l < r l\lt r l<r),求 f ( s l s l + 1 … s r ) f(s_ls_{l+1}\ldots s_r) f(slsl+1…sr)的值。
† ^\dagger † 字符串 z z z的子串是来自 z z z的连续字符段。例如,“ d e f o r \mathtt{defor} defor”、" c o d e \mathtt{code} code“和” o \mathtt{o} o“都是” c o d e f o r c e s \mathtt{codeforces} codeforces“的子串,而” c o d e s \mathtt{codes} codes“和” a a a \mathtt{aaa} aaa"不是。
‡ ^\ddagger ‡ 回文字符串是指前后读法相同的字符串。例如,字符串" z \texttt{z} z“、” aa \texttt{aa} aa“和” tacocat \texttt{tacocat} tacocat“是回文字符串,而” codeforces \texttt{codeforces} codeforces“和” ab \texttt{ab} ab"不是。
分析:
反着考虑全为回文串需满足哪些情况:
- k = 1 k=1 k=1 没有限制
- k = 2 k=2 k=2 整个区间全部相同
- k = 3 k=3 k=3 交替出现
- k > 3 k\gt3 k>3 递推一下可以发现奇数需交替排列或全相等,偶数只能全相等。
特殊情况,若 k = r − l + 1 k=r-l+1 k=r−l+1,那么只要整段不回文,就有 r − l + 1 r-l+1 r−l+1的贡献
判断区间是否回文可以使用马拉车算法或者字符串哈希。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
vector<LL> manacher_odd(string s) {
LL n = s.size();
s = "$" + s + "^";
vector<LL> p(n + 2);
LL l = 1, r = 1;
for (LL i = 1; i <= n; i++) {
p[i] = max(0ll, min(r - i, p[l + (r - i)]));
while (s[i - p[i]] == s[i + p[i]]) {
p[i]++;
}
if (i + p[i] > r) {
l = i - p[i], r = i + p[i];
}
}
return vector<LL>(begin(p) + 1, end(p) - 1);
}
vector<LL> manacher(string s) {
string t;
for (auto c: s) {
t += string("#") + c;
}
auto res = manacher_odd(t + "#");
return vector<LL>(begin(res) + 1, end(res) - 1);
}
void Solve() {
LL n, q;
cin >> n >> q;
string s;
cin >> s;
auto v = manacher(s);
for (auto &x: v)
x--;
set<LL> s1, s2;
for (LL i = 0; i < n - 1; i++) {
if (s[i] != s[i + 1])
s1.insert(i);
if (i != n - 1 && s[i] != s[i + 2])
s2.insert(i);
}
while (q--) {
LL l, r;
cin >> l >> r;
l--;
r--;
if (l == r) {
cout << 0 << endl;
continue;
}
LL len = r - l + 1;
LL ans;
auto it = s1.lower_bound(l);
if (it == s1.end() || (*it) >= r) {
ans = 0;
} else {
it = s2.lower_bound(l);
if (it == s2.end() || (*it) >= r - 1) {
ans = ((len - 1) / 2) * (((len - 1) / 2) + 1);
} else {
ans = len * (len - 1) / 2 - 1;
}
}
if (v[l + r] < (r - l + 1))
ans += len;
cout << ans << endl;
}
}
int main() {
LL t;
cin >> t;
while (t--) {
Solve();
}
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。