A.Maximize?(枚举)
题意:
给你一个整数xxx。你的任务是找出任意一个整数yyy(1≤y<x)(1\le y\lt x)(1≤y<x),使得gcd(x,y)+y\gcd(x,y)+ygcd(x,y)+y为最大可能数。(1≤y<x)(1\le y\lt x)(1≤y<x)使得gcd(x,y)+y\gcd(x,y)+ygcd(x,y)+y最大。
注意,如果有一个以上的yyy满足该语句,允许找到任何一个。
gcd(a,b)\gcd(a,b)gcd(a,b)是aaa和bbb的最大公约数。例如,gcd(6,4)=2\gcd(6,4)=2gcd(6,4)=2。
分析:
本题数据范围较小,直接暴力枚举即可。
此外,因为y<xy\lt xy<x所以gcd(x,y)\gcd(x,y)gcd(x,y)等价于gcd(x,x−y)\gcd(x,x-y)gcd(x,x−y),易得gcd(x,x−y)≤x−y\gcd(x,x-y)\le x-ygcd(x,x−y)≤x−y,所以gcd(x,x−y)+y≤x\gcd(x,x-y)+y\le xgcd(x,x−y)+y≤x,又因为相邻两个数的gcd\gcdgcd为111,所以当y=x−1y=x-1y=x−1时一定满足题目,可以直接输出x−1x-1x−1。
代码:
#include<bits/stdc++.h>
using namespace std;
int gcd(int a, int b) {
if (b > a) swap(a, b);
if (b == 0) return a;
return gcd(b, a % b);
}
int main() {
int t;
cin >> t;
while (t--) {
int x;
cin >> x;
int res = 0;
int ans = 1;
for (int y = 1; y < x; y++) {
if (gcd(x, y) + y > res) {
res = gcd(x, y) + y;
ans = y;
}
}
cout << ans << endl;
}
return 0;
}
B.Prefiquence(双指针)
题意:
给你两个二进制字符串aaa和bbb。二进制字符串是由字符"0"和"1"组成的字符串。
你的任务是确定最大可能的数字kkk,使得长度为kkk的字符串aaa的前缀是字符串bbb的子序列。
如果aaa可以从bbb中删除几个(可能是零个或全部)元素,那么序列aaa就是序列bbb的子序列。
分析:
本题本质就是找bbb字符串的子序列作为aaa的前缀最大是多少。
考虑双指针,设置lll指针指向aaa字符串的第一个字母 rrr指针指向bbb字符串的第一个字母,若lll和rrr指向的字母相同,则双指针往后移动,并更新ansansans值为lll指针 若lll和rrr不相同 则移动rrr指针至第一个可以和lll指针匹配的位置,逐个移动直到lll或者rrr指针跑到nnn或mmm。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
void solve() {
LL n, m;
cin >> n >> m;
string a, b;
cin >> a >> b;
a = " " + a;
b = " " + b;
LL l, r;
LL ans = 0;
l = r = 1;
while (l <= n && r <= m) {
if (a[l] == b[r]) {
l++;
r++;
ans = l;
} else if (a[l] != b[r]) {
while (a[l] != b[r] && r <= m) {
r++;
}
}
}
ans = max(ans - 1, 0LL);
cout << ans << endl;
}
int main() {
LL t;
cin >> t;
while (t--)
solve();
return 0;
}
C.Assembly via Remainders(构造)
题意:
给你一个数组x2,x3,…,xnx_2,x_3,\dots,x_nx2,x3,…,xn。你的任务是找出任意一个数组a1,…,ana_1,\dots,a_na1,…,an,其中:
- 对于所有的1≤i≤n1\le i\le n1≤i≤n,1≤ai≤1091\le a_i\le 10^91≤ai≤109。
- 对于所有2≤i≤n2\le i\le n2≤i≤n,xi=ai mod ai−1x_i=a_i\bmod a_{i-1}xi=aimodai−1。
这里的c mod dc\bmod dcmodd表示整数ccc除以整数ddd的余数。例如,5 mod 2=15\bmod 2=15mod2=1,72 mod 3=072\bmod 3=072mod3=0,143 mod 14=3143\bmod 14=3143mod14=3。
注意,如果有一个以上的aaa满足该语句的要求,你可以找到任何一个。
分析:
题目要求构造aia_iai,考虑如果ai−1a_{i-1}ai−1比aia_iai大的话,根据模的计算方法,可以直接放入xix_ixi。
我们每次构造出的aia_iai,都让它等于ai−1+xia_{i-1}+x_iai−1+xi,让数组aaa一直递增,即可符合要求。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL N = 505;
int x[N], a[N];
void solve() {
int n;
cin >> n;
for (int i = 2; i <= n; i++)
cin >> x[i];
a[n] = 1e9;
for (int i = n; i >= 2; i--) {
if (x[i] == a[i]) {
a[i - 1] = 1e9;
continue;
}
a[i - 1] = a[i] - x[i];
}
for (int i = 1; i <= n; i++) {
cout << a[i] << " ";
}
cout << endl;
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
D.Permutation Game(贪心)
题意:
Bodya和Sasha发现了一个排列p1,…,pnp_1,\dots,p_np1,…,pn和一个数组a1,…,ana_1,\dots,a_na1,…,an。他们决定玩一个著名的 “排列游戏”。
长度为nnn的排列是由nnn个不同的整数组成的数组,这些整数从111到nnn按任意顺序排列。例如,[2,3,1,5,4][2,3,1,5,4][2,3,1,5,4]是一个排列,但[1,2,2][1,2,2][1,2,2]不是一个排列(222在数组中出现了两次),[1,3,4][1,3,4][1,3,4]也不是一个排列(n=3n=3n=3,但数组中有444)。
它们都在排列中选择了一个起始位置。
对局持续了kkk个回合。棋手同时下棋。在每个回合中,每个棋手都会发生两件事:
- 如果棋手当前的位置是xxx,他的得分就会增加axa_xax。
- 然后棋手要么停留在当前位置xxx,要么从xxx移动到pxp_xpx。
在整整kkk个回合后,得分较高的一方即为获胜者。
知道了Bodya的起始位置PBP_BPB和Sasha的起始位置PSP_SPS后,如果两位棋手都想获胜,那么谁会赢得对局?
分析:
最优的方案一定是先走几步,然后一直停在某个点,如果我们站的点是最大的,那么我们就不需要移动,因为可以一直站在上面得到最大的分数。
否则我们就去下一个位置去看看,能不能获得更多的得分。
遍历的话因为是排列,移动是一个环,所以最多的遍历次数是nnn。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
void solve() {
LL n, k, pb, ps;
cin >> n >> k >> pb >> ps;
vector<LL> p(n + 1), a(n + 1);
for (int i = 1; i <= n; i++)
cin >> p[i];
for (int i = 1; i <= n; i++)
cin >> a[i];
LL ans_pb = 0, ans_ps = 0;
LL res_pb = 0, res_ps = 0;
for (int i = 0; i < min(k, n); i++) {
res_pb += a[pb];
ans_pb = max(ans_pb, res_pb + (k - i - 1) * a[pb]);
pb = p[pb];
res_ps += a[ps];
ans_ps = max(ans_ps, res_ps + (k - i - 1) * a[ps]);
ps = p[ps];
}
if (ans_pb > ans_ps)
cout << "Bodya" << endl;
else if (ans_pb == ans_ps)
cout << "Draw" << endl;
else
cout << "Sasha" << endl;
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
E.Cells Arrangement(构造)
题意:
给你一个整数nnn。您在网格n×nn\times nn×n中选择nnn个单元格(x1,y1),(x2,y2),…,(xn,yn)(x_1,y_1),(x_2,y_2),\dots,(x_n,y_n)(x1,y1),(x2,y2),…,(xn,yn),其中1≤xi≤n1\le x_i\le n1≤xi≤n和1≤yi≤n1\le y_i\le n1≤yi≤n。
设H\mathcal{H}H为任意一对单元格之间不同的曼哈顿距离集合。你的任务是最大化H\mathcal{H}H的大小。注释中给出了集合及其构造的例子。
如果存在不止一个解,你可以输出任意一个。
单元格(x1,y1)(x_1,y_1)(x1,y1)和(x2,y2)(x_2,y_2)(x2,y2)之间的曼哈顿距离等于∣x1−x2∣+∣y1−y2∣|x_1-x_2|+|y_1-y_2|∣x1−x2∣+∣y1−y2∣。
分析:
观察样例发现,在(1,1)(1,1)(1,1)放一个,然后(1,2)(1,2)(1,2)下面放一个,如果有多的,全部斜着放是能取完所有的距离情况的,这样放是最大值。
代码:
#include<bits/stdc++.h>
using namespace std;
void solve() {
int n;
cin >> n;
cout << "1 1" << endl;
cout << "1 2" << endl;
for (int i = 3; i <= n; i++) {
cout << i << " " << i << endl;
}
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
F.Equal XOR Segments(前缀处理)
题意:
如果可以将数组分成k>1k\gt 1k>1部分,使得每一部分值按位异或都相等,那么我们就称这个数组为x1,…,xmx_1,\dots,x_mx1,…,xm有趣。
更正式地说,你必须把数组xxx分成kkk个连续的部分,xxx中的每个元素都必须准确地属于111部分。设y1,…,yky_1,\dots,y_ky1,…,yk分别是各部分元素的XOR。那么y1=y2=⋯=yky_1=y_2=\dots=y_ky1=y2=⋯=yk必须满足。
例如,如果是x=[1,1,2,3,0]x=[1,1,2,3,0]x=[1,1,2,3,0],可以将其拆分如下:[1],[1],[2,3,0][\color{blue}1],[\color{green}1],[\color{red}2,\color{red}3,\color{red}0][1],[1],[2,3,0]。事实上是1=1=2⊕3⊕0\color{blue}1=\color{green}1=\color{red}2\oplus\color{red}3\oplus\color{red}01=1=2⊕3⊕0。
给你一个数组a1,…,ana_1,\dots,a_na1,…,an。你的任务是回答qqq次查询:
- 对于固定的lll,rrr, 判断子数组al,al+1,…,ara_l,a_{l+1},\dots,a_ral,al+1,…,ar是否有趣。
分析:
区间问题先转化为前缀异或。
- Sr⊕Sl−1=0S_r\oplus S_{l-1}=0Sr⊕Sl−1=0
题目保证了l<rl\lt rl<r,所以一定有解。
- Sr⊕Sl−1=vS_r\oplus S_{l-1}=vSr⊕Sl−1=v
区间一定可以划分为奇数段。如果能划分成奇数段,则一定能划分成333段。
最终划分的异或值一定是vvv。
于是问题转变为是否能把[l,r][l,r][l,r]分成三段,使得每段异或值都为vvv。
找到最大的i<ri\lt ri<r使得Sr⊕Si=vS_r\oplus S_i=vSr⊕Si=v,如果找不到则无解。
找到最大的j<ij\lt ij<i使得Si⊕Sj=vS_i\oplus S_j=vSi⊕Sj=v,如果找不到或则j<l−1j\lt l-1j<l−1无解。
具体实现可以维护一个map<int, vector<int> >来实现。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
void solve() {
LL n, q;
cin >> n >> q;
vector<LL> a(n + 1);
map<int, vector<int>> mp;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++) {
a[i] ^= a[i - 1];
mp[a[i]].push_back(i);
}
while (q--) {
int l, r;
cin >> l >> r;
LL p = a[r] ^ a[l - 1];
if (p == 0) {
cout << "YES" << endl;
} else {
auto &v1 = mp[a[r]];
auto pl = lower_bound(v1.begin(), v1.end(), l);
if (pl == v1.end() || *pl >= r) {
cout << "NO" << endl;
continue;
}
LL posl = *pl;
auto &v2 = mp[a[l - 1]];
auto pr = lower_bound(v2.begin(), v2.end(), posl + 1);
if (pr != v2.end() && *pr < r) {
cout << "YES" << endl;
} else
cout << "NO" << endl;
}
}
}
int main() {
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}
G1.Division + LCP (easy version)(LCP)
题意:
这是问题的简单版本。在这个版本中l=rl=rl=r
给你一个字符串sss。对于固定的kkk,考虑将sss恰好分成kkk个连续的子串w1,…,wkw_1,\dots,w_kw1,…,wk。假设fkf_kfk是所有分割中最大的LCP(w1,…,wk)LCP(w_1,\dots,w_k)LCP(w1,…,wk)。
LCP(w1,…,wm)LCP(w_1,\dots,w_m)LCP(w1,…,wm)是字符串w1,…,wmw_1,\dots,w_mw1,…,wm的最长公共前缀的长度。
例如,如果s=abababcabs=abababcabs=abababcab和k=4k=4k=4,可能的除法是abababcab\color{red}{ab}\color{blue}{ab}\color{orange}{abc}\color{green}{ab}abababcab。由于ababab是这四个字符串的最长公共前缀,因此LCP(ab,ab,abc,ab)LCP(\color{red}{ab},\color{blue}{ab},\color{orange}{abc},\color{green}{ab})LCP(ab,ab,abc,ab)就是222。请注意,每个子串都由一段连续的字符组成,每个字符都属于一个子串。
您的任务是找出fl,fl+1,…,frf_l,f_{l+1},\dots,f_rfl,fl+1,…,fr。在此版本中为l=rl=rl=r。
分析:
如果存在划分使得LCPLCPLCP为vvv,则任意v‘<vv \text{`} \lt vv‘<v都存在划分。
存在单调性,可以二分前缀长度。
令前缀字符串为ttt,sss种最多有xxx个不相交的子串ttt。
如果x≥kx\ge kx≥k,则存在划分使得LCPLCPLCP为ttt。
可以用kmpkmpkmp或字符串哈希求出。
代码:
#include<bits/stdc++.h>
using namespace std;
void solve() {
int n, _, k;
string s;
cin >> n >> _ >> k >> s;
vector<int> ne(n + 1, 0);
ne[0] = -1;
for (int i = 1, j = -1; i < n; i++) {
while (j >= 0 && s[j + 1] != s[i])
j = ne[j];
if (s[j + 1] == s[i])
j++;
ne[i] = j;
}
auto check = [&](int m) -> bool {
if (m == 0)
return 1;
string t = s.substr(0, m);
int tmp = 0;
for (int i = 0, j = -1; i < n; i++) {
while (j != -1 && s[i] != t[j + 1])
j = ne[j];
if (s[i] == t[j + 1])
j++;
if (j == m - 1) {
tmp++;
j = -1;
}
}
return tmp >= k;
};
int l = 0, r = n / k;
while (l < r) {
int mid = l + r + 1 >> 1;
if (check(mid))
l = mid;
else
r = mid - 1;
}
cout << l << endl;
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

1005

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



