A.Destroying Bridges(思维)
题意:
有nnn个岛屿,编号为1,2,…,n1,2,\ldots,n1,2,…,n。最初,每对岛屿都由一座桥连接。因此,一共有n(n−1)2\frac{n(n-1)}{2}2n(n−1)座桥。
Everule住在111号岛上,喜欢利用桥梁访问其他岛屿。Dominater有能力摧毁最多kkk座桥梁,以尽量减少Everule可以使用(可能是多座)桥梁到达的岛屿数量。
如果Dominater以最佳方式摧毁桥梁,求Everule可以访问的岛屿(包括岛屿111)的最少数量。
分析:
本题岛屿和桥构成一个完全图,每个点的连边数为n−1n-1n−1,要么至少摧毁111号岛连接其他n−1n-1n−1座岛的所有n−1n-1n−1座桥,这样只能访问111岛,要么所有岛都能访问。
代码:
#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(数学)
题意:
给你一个长度为2n2n2n的数组aaa,其中从111到nnn的每个整数都出现两次。
同时给你一个整数kkk(1≤k≤⌊n2⌋1\leq k\leq\lfloor\frac{n}{2}\rfloor1≤k≤⌊2n⌋)。
你需要找到两个长度分别为2k\mathbf{2k}2k的数组lll和rrr,使得:
- lll是[a1,a2,…an][a_1,a_2,\ldots a_n][a1,a2,…an]的子集†^\dagger†。
- rrr是[an+1,an+2,…a2n][a_{n+1},a_{n+2},\ldots a_{2n}][an+1,an+2,…a2n]的子集
- lll中元素的按位异或等于rrr中元素的按位异或;换句话说,l1⊕l2⊕…⊕l2k=r1⊕r2⊕…⊕r2kl_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
可以证明至少有一对lll和rrr总是存在的。如果有多个解,可以输出其中任意一个。
†^\dagger† 序列xxx是序列yyy的子集,如果xxx可以通过删除yyy中的几个元素(可能一个元素也没有或全部元素)并按任意顺序重新排列而得到。例如,[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,2n)(n+1,2n)(n+1,2n)
- 一个出现在(1,n)(1,n)(1,n),另一个出现在(n+1,2n)(n+1,2n)(n+1,2n)
前两种情况数目是相同的。我们先用前两种成对分别填入l,rl,rl,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)
题意:
爱丽丝和鲍勃在大小为nnn的数组aaa上进行另一场博弈。爱丽丝从一个空数组ccc开始。双方轮流下棋,爱丽丝先开始。
轮到爱丽丝时,她从aaa中选取一个元素,将其追加到ccc中,然后从aaa中删除。
轮到鲍勃时,他从aaa中选取一个元素,然后从aaa中删除。
当数组aaa为空时,游戏结束。游戏的分数定义为ccc的MEX †^\dagger† 。爱丽丝希望最大化得分,而鲍勃希望最小化得分。如果双方都以最佳状态进行游戏,求游戏的最终得分。
†^\dagger†整数数组的MEX\operatorname{MEX}MEX(最小不等式)定义为数组中不出现的最小非负整数。例如
- [2,2,1][2,2,1][2,2,1]的MEX\operatorname{MEX}MEX是000,因为000不属于数组。
- [3,1,0,1][3,1,0,1][3,1,0,1]的MEX\operatorname{MEX}MEX是222,因为000和111属于数组,而222不属于数组。
- [0,3,1,2][0,3,1,2][0,3,1,2]的MEX\operatorname{MEX}MEX是444,因为000、111、222和333属于数组,而444不属于数组。
分析:
出现次数大于111次的,无论先后手爱丽丝肯定都能抢到。
由于先手的缘故,爱丽丝可以保证将一个出现次数为111的数拿到手,显然开局拿最小的且出现次数为111的数是最优的,所以我们按照MEX\operatorname{MEX}MEX,从小到大优先拿出现次数为111的数,然后从小到大找第一个拿不到的数即可。
代码:
#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)
题意:
如果至少存在一个长度为kkk的子串†^\dagger†不是回文‡^\ddagger‡,则称字符串ttt为好kkk字符串。令f(t)f(t)f(t)表示所有kkk的值之和,使得字符串ttt是好kkk字符串。
给你一个长度为nnn的字符串sss。回答qqq个问题:
- 给定lll和rrr(l<rl\lt rl<r),求f(slsl+1…sr)f(s_ls_{l+1}\ldots s_r)f(slsl+1…sr)的值。
†^\dagger† 字符串zzz的子串是来自zzz的连续字符段。例如,“defor\mathtt{defor}defor”、"code\mathtt{code}code“和”o\mathtt{o}o“都是”codeforces\mathtt{codeforces}codeforces“的子串,而”codes\mathtt{codes}codes“和”aaa\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=1k=1k=1 没有限制
- k=2k=2k=2 整个区间全部相同
- k=3k=3k=3 交替出现
- k>3k\gt3k>3 递推一下可以发现奇数需交替排列或全相等,偶数只能全相等。
特殊情况,若k=r−l+1k=r-l+1k=r−l+1,那么只要整段不回文,就有r−l+1r-l+1r−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,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

1082

被折叠的 条评论
为什么被折叠?



