A.Sakurako’s Exam (思维)
题意:
S a k u r a k o Sakurako Sakurako有一场数学考试。老师给了一个数组,由 a a a个 1 和 b b b个 2 组成。
在数组中,
S
a
k
u
r
a
k
o
Sakurako
Sakurako必须在每个元素前面放置一个 +
或 -
,以使数组中所有元素的总和等于
0
0
0。
S a k u r a k o Sakurako Sakurako不确定是否有可能解决这个问题,因此确定是否有办法分配符号,使得数组中所有元素的总和等于 0 0 0。
分析:
当 a , b a,b a,b都为偶数时候,一定可以使得和为 0 0 0。只需要对半符号相反即可。当 a a a为偶数, b b b可以是奇数也可以是偶数。例如1 1 2符合条件,1 1 2 2也符合条件。当 a a a是奇数个时,不满足题意。当 a , b a,b a,b其中一个为 0 0 0时,另外一个要为偶数个。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int N = 3e5 + 10;
const int InF = 2e9 + 5;
const int mod = 998244353;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
int a, b;
cin >> a >> b;
if (a == 0)
{
if (b % 2)
cout << "NO" << endl;
else
cout << "YES" << endl;
}
else if (b == 0)
{
if (a % 2)
cout << "NO" << endl;
else
cout << "YES" << endl;
}
else if (a % 2)
cout << "NO" << endl;
else
cout << "YES" << endl;
}
return 0;
}
B.Square or Not (思维)
题意:
精美的二进制矩阵是边缘为 1 1 1而内部为 0 0 0的矩阵。 S a k u r a k o Sakurako Sakurako有一个大小为 r × c r \times c r×c的精美二进制矩阵,并通过写下矩阵的所有行(从第一行开始到第 r r r行结束)创建了一个二进制字符串 s s s。换句话说,矩阵中第 i i i行和 j j j列的元素对应于字符串的第 ( ( i − 1 ) ∗ c + j ) ((i-1)*c+j) ((i−1)∗c+j)个元素。 现在你需要检查字符串 s s s所构造的矩阵是不是正方形。
分析:
首先考虑读入的字符串长度是否能构造成正方形,其次因为矩阵的外围为 1 1 1,中间为 0 0 0,遍历到 时 ( i , j ) (i,j) (i,j),看字符串对应位置 i × n + j i \times \sqrt{n} + j i×n+j的字符是否为对应字符即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int N = 3e5 + 10;
const int InF = 2e9 + 5;
const int mod = 998244353;
void solve()
{
int n;
string s;
cin >> n >> s;
int m = sqrt(n);
if (m * m != n)
{
cout << "No" << endl;
return;
}
for (int i = 0; i < m; i++)
{
for (int j = 0; j < m; j++)
{
char c = '0';
if (!i || !j || i == m - 1 || j == m - 1)
c = '1';
if (s[i * m + j] != c)
{
cout << "No" << endl;
return;
}
}
}
cout << "Yes" << endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
C. Longest Good Array (贪心)
题意:
S a k u r a k o Sakurako Sakurako正在研究数组。长度为 n n n的数组 a a a被认为是好的,当且仅当:
-
数组 a a a是递增的,并且对于所有 2 ≤ i ≤ n 2 \le i \le n 2≤i≤n都是 a _ i − 1 < a _ i a\_{i - 1} < a\_i a_i−1<a_i;
-
相邻元素之间的差异是递增的,意味着对于所有 2 ≤ i < n 2 \le i < n 2≤i<n都是 a _ i − a _ i − 1 < a _ i + 1 − a _ i a\_i - a\_{i-1} < a\_{i+1} - a\_i a_i−a_i−1<a_i+1−a_i。
S a k u r a k o Sakurako Sakurako已经确定了 l l l和 r r r,并希望构造一个长度最大的好数组,其中对于所有 a _ i a\_i a_i都是 l ≤ a _ i ≤ r l \le a\_i \le r l≤a_i≤r。
帮助 S a k u r a k o Sakurako Sakurako找到给定 l l l和 r r r的好数组的最大长度。
分析:
我们贪心的考虑这个问题,让差值每次递增 1 1 1,例如 l , l + 1 , l + 3 , l + 6 l,l+1,l+3,l+6 l,l+1,l+3,l+6这样进行构造,再二分答案能构造的最大长度即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int N = 3e5 + 10;
const int InF = 2e9 + 5;
const int mod = 998244353;
void solve()
{
LL L, R;
cin >> L >> R;
LL l = 0, r = R;
while (l < r)
{
LL mid = l + r + 1 >> 1;
LL sum = (1 + mid) * mid / 2;
if (L + sum <= R)
l = mid;
else
r = mid - 1;
}
cout << r + 1 << endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
D. Sakurako’s Hobby (枚举)
题意:
对于某个排列 p p p, S a k u r a k o Sakurako Sakurako规定整数 j j j可以通过 i i i到达,当且仅当可以通过进行若干次 i = p _ i i=p\_i i=p_i的操作 ,使 i i i等于 j j j。
如果是 p = [ 3 , 5 , 6 , 1 , 2 , 4 ] p=[3,5,6,1,2,4] p=[3,5,6,1,2,4],那么例如, 4 4 4可以从 1 1 1到达,因为: i = 1 i=1 i=1 → \rightarrow → i = p _ 1 = 3 i=p\_1=3 i=p_1=3 → \rightarrow → i = p _ 3 = 6 i=p\_3=6 i=p_3=6 → \rightarrow → i = p _ 6 = 4 i=p\_6=4 i=p_6=4。现在为 i = 4 i=4 i=4,因此 4 4 4可从 1 1 1到达。
排列中的每个数字都被涂成黑色或白色。
S a k u r a k o Sakurako Sakurako将函数 F ( i ) F(i) F(i)定义为可从 i i i到达的黑色整数的数量。
请计算出每个 1 ≤ i ≤ n 1\le i\le n 1≤i≤n的 F ( i ) F(i) F(i)的值。
分析:
考虑到题目给出的是排列,那么所有元素在按照规则遍历时,会出现环状结构(最后到达本身),这个环上的所有元素 F ( i ) F(i) F(i)都相等。我们逐个循环枚举就可以求出所有的 F ( i ) F(i) F(i)。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int N = 3e5 + 10;
const int InF = 2e9 + 5;
const int mod = 998244353;
void solve()
{
LL n;
cin >> n;
LL p[n + 1] = {0}, b[n + 1] = {0};
int us[n + 1] = {0};
for (int i = 1; i <= n; i++)
{
cin >> p[i];
}
string s;
cin >> s;
for (int i = 1; i <= n; i++)
{
if (us[i])
continue;
int sz = 0;
while (!us[i])
{
us[i] = 1;
sz += s[i - 1] == '0';
i = p[i];
}
while (us[i] != 2)
{
b[i] = sz;
us[i] = 2;
i = p[i];
}
}
for (int i = 1; i <= n; i++)
{
cout << b[i] << " ";
}
cout << endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
E. Alternating String (dp)
题意:
S a k u r a k o Sakurako Sakurako非常喜欢交替字符串。如果偶数位置上的字符相同,奇数位置上的字符相同,并且字符串的长度为偶数,她将小写拉丁字母的字符串 s s s称为交替字符串。
例如,字符串 abab
和 gg
是交替的,而字符串 aba
和 ggwp
则不是。
现在,你可以对字符串执行两种类型的操作:
-
选择一个索引 i i i,并从字符串中删除第 i i i个字符,这将使字符串的长度减少 1 1 1。这种类型的操作最多只能执行 1 1 1次;
-
选择索引 i i i,并将 s _ i s\_i s_i替换为任何其他字母。
给出使字符串变为交替字符串所需的最少操作数。
分析:
因为只能删除一次字符,所以长度为奇数时才考虑删除。我们令 n u m [ i ] [ j ] num[i][j] num[i][j]表示奇数位或者偶数位,各种字符出现的次数。当 n n n为偶数时,分别找到奇数位与偶数位出现最多字符的次数,这些字符保持不变。那么有 a n s = n − m a x ( n u m _ 0 , n u m _ 0 + 25 ) − m a x ( n u m _ 1 , n u m _ 1 + 25 ) ; ans=n-max(num\_0,num\_0+25)-max(num\_1,num\_1+25); ans=n−max(num_0,num_0+25)−max(num_1,num_1+25);。当 n n n为奇数时,我们只要遍历 1 − n 1-n 1−n,表示删除该字符后的操作次数,最终去最小值即可。 我们再令 t m p [ i ] [ j ] tmp[i][j] tmp[i][j]表示在当前位置上各个字符出现的次数,再令 c n t [ i ] [ j ] cnt[i][j] cnt[i][j]表示删除该位置上字符后整个字符串上奇偶字符数。那么有 c n t [ 0 ] [ j ] = t m p [ 0 ] [ j ] + n u m [ 1 ] [ j ] − t m p [ 1 ] [ j ] cnt[0][j]=tmp[0][j]+num[1][j]-tmp[1][j] cnt[0][j]=tmp[0][j]+num[1][j]−tmp[1][j]。 c n t [ 1 ] [ j ] = t m p [ 1 ] [ j ] + n u m [ 0 ] [ j ] − t m p [ 0 ] [ j ] cnt[1][j]=tmp[1][j]+num[0][j]-tmp[0][j] cnt[1][j]=tmp[1][j]+num[0][j]−tmp[0][j]。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int N = 3e5 + 10;
const int InF = 2e9 + 5;
const int mod = 998244353;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
string s;
cin >> s;
int ans = n;
int cnt[2][26]{};
for (int i = 0; i < n; i++)
{
cnt[i % 2][s[i] - 'a']++;
}
if (n % 2 == 0)
{
ans = ans - *max_element(cnt[0], cnt[0] + 26) - *max_element(cnt[1], cnt[1] + 26);
}
for (int i = n - 1; i >= 0; i--)
{
cnt[i % 2][s[i] - 'a']--;
if (n % 2)
{
ans = min(ans, n - *max_element(cnt[0], cnt[0] + 26) - *max_element(cnt[1], cnt[1] + 26));
}
cnt[(i + 1) % 2][s[i] - 'a']++;
}
cout << ans << endl;
}
return 0;
}
F. Sakurako’s Box (数学)
题意:
S a k u r a k o Sakurako Sakurako有一个装有 n n n个球的盒子。每个球都有其价值。她想和朋友打赌,朋友能否从盒子中随机挑选两个球(可能是两个不同的球,但它们的价值可能相同),它们的价值乘积将与 S a k u r a k o Sakurako Sakurako猜测的数字相同。 现在你要帮他找出数组中两个元素乘积的期望值。 可以证明期望值的形式为 P Q \frac{P}{Q} QP,其中 P P P和 Q Q Q为非负整数,并且为 Q ≠ 0 Q \ne 0 Q=0。输出 P ⋅ Q − 1 ( m o d 1 0 9 + 7 ) P \cdot Q^{-1}(\bmod 10^9+7) P⋅Q−1(mod109+7)的值。
分析:
数组中数对的数量为 C _ n 2 = n ∗ ( n − 1 ) 2 C\_n^2 = \frac{n*(n-1)}{2} C_n2=2n∗(n−1),所有数对的乘积和为 s u m = ∑ _ i = 2 n ∑ _ j = 1 i − 1 a _ j ∗ a _ i sum = \sum\_{i=2}^n \sum\_{j=1}^{i-1}a\_j*a\_i sum=∑_i=2n∑_j=1i−1a_j∗a_i,那么期望值就是 s u m C 2 _ n \frac{sum}{C^2\_{n}} C2_nsum。我们再用前缀和进行优化,令 f ( i ) = ∑ _ j = 1 i a _ i f(i) = \sum\_{j=1}^i a\_i f(i)=∑_j=1ia_i, s u m = ∑ _ i = 2 n a _ i ∗ ∑ _ j = 1 i − 1 a _ j = ∑ _ i = 2 n a _ i ∗ f ( i − 1 ) sum=\sum\_{i=2}^n a\_i *\sum\_{j=1}^{i-1}a\_j = \sum\_{i=2}^n a\_i * f(i-1) sum=∑_i=2na_i∗∑_j=1i−1a_j=∑_i=2na_i∗f(i−1)。再用快速幂算出逆元进行计算即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int N = 3e5 + 10;
const int InF = 2e9 + 5;
const int mod = 1e9 + 7;
LL qmi(LL a, int b)
{
LL res = 1;
while (b)
{
if (b & 1)
res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
LL n;
cin >> n;
LL pre = 0, sum = 0;
for (int i = 1; i <= n; i++)
{
LL x;
cin >> x;
if (i > 1)
{
(sum += pre * x % mod) %= mod;
}
(pre += x) %= mod;
}
LL ans = sum * qmi(n * (n - 1) % mod * qmi(2, mod - 2) % mod, mod - 2) % mod;
cout << ans << endl;
}
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。