前言
个人认为比普及组难度要略低,但实际测试结果极不理想。
题解
以下是这场比赛的各题题解。
#5101. YAOI Summer Round #4 (Div 2) A. 子串之美
得分率正常。
对于 (20\%) 的数据,考虑直接枚举子串,再加上一些剪枝,考场上果然也没人写。
对于 (50\%) 的数据,考虑用 KMP 算法去统计每个子串,可以做到 (O(nlog n)) 的复杂度。
对于另外 (10\%) 的数据,显然 a
是出现次数最多的非空子串,出现次数为 (n)。
对于 (100\%) 的数据,显然出现次数最多的非空子串长度为 (1),于是用桶存一下每个字母的出现次数即可。
#include <bits/stdc++.h>
#define Re register
using namespace std;
int n, d[31], ans;
char s[10000005];
int main() {
scanf("%d", &n);
scanf("%s", s + 1);
for (Re int i = 1; i <= n; i++) {
d[s[i] - 'a']++;
}
for (Re int i = 0; i < 26; i++) {
ans = max(ans, d[i]);
}
printf("%d", ans);
return 0;
}
#5102. YAOI Summer Round #4 (Div 2) B. 集合与映射
得分率略低于预期。
对于 (30\%) 的数据,分别求出 (A,B,C) 即可,复杂度为 (O(n^2))。
对于另外 (20\%) 的数据,有 (10\%) 为单调递增,有 (10\%) 为单调递减。
单调递增的那部分,显然可以得到答案为 (0)。
单调递减的那部分,如果有用心去算的话,很容易发现是序列的逆序对数。
对于 (100\%) 的数据,我们通过大力分类讨论(大体上分出了 (7) 类,读者可以尝试自行证明),可以证明就是序列中逆序对的数量。
故可以在 (O(nlog n)) 的复杂度内完成本题。
#include <bits/stdc++.h>
#define Re register
using namespace std;
typedef long long ll;
const int N = 1000005;
int n, f[N];
ll c[N], ans;
inline int lb(int x) { return x & -x; }
inline void add(int p) {
for (; p <= n; p += lb(p)) c[p]++;
}
inline ll qry(int p) {
ll res = 0;
for (; p; p -= lb(p)) res += c[p];
return res;
}
int main() {
scanf("%d", &n);
for (Re int i = 1; i <= n; i++) {
scanf("%d", &f[i]);
}
for (Re int i = 1; i <= n; i++) {
add(f[i]);
ans += i - qry(f[i]);
}
printf("%lld", ans);
return 0;
}
#5103. YAOI Summer Round #4 (Div 2) C. 取物游戏
得分率远低于预期。
对于 (20\%) 的数据,直接模拟题意爆搜即可。
对于 (50\%) 的数据,是写正解做法被卡时间或空间所得到的分。
对于 (100\%) 的数据,考虑设 (dp[i][j][k][0/1]) 表示在点 ((i,j)) 处,差值为 (k),这一步是 Alice 取还是 Bob 取的方案数。(如果状态不小心多设了一维,就变成 (50\%) 的那一档)
转移很容易想到,具体见代码。
时间复杂度为 (O(nmk)),空间复杂度为 (O(nmk))。
#include <bits/stdc++.h>
#define Re register
using namespace std;
const int N = 605, K = 35;
const int mod = 1000000007;
int n, m, k, ans;
int a[N][N];
int dp[N][N][K][2];
int main() {
scanf("%d%d%d", &n, &m, &k);
for (Re int i = 1; i <= n; i++) {
for (Re int j = 1; j <= m; j++) {
scanf("%d", &a[i][j]);
a[i][j] %= k;
dp[i][j][a[i][j]][1] = 1;
}
}
for (Re int i = 1; i <= n; i++) {
for (Re int j = 1; j <= m; j++) {
for (Re int s = k; s >= 0; s--) {
(dp[i][j][s][1] += dp[i - 1][j][(s + k - a[i][j]) % k][0]) %= mod;
(dp[i][j][s][1] += dp[i][j - 1][(s + k - a[i][j]) % k][0]) %= mod;
(dp[i][j][s][0] += dp[i - 1][j][(s + a[i][j]) % k][1]) %= mod;
(dp[i][j][s][0] += dp[i][j - 1][(s + a[i][j]) % k][1]) %= mod;
}
}
}
for (Re int i = 1; i <= n; i++) {
for (Re int j = 1; j <= m; j++) {
ans = (ans + dp[i][j][0][0]) % mod;
}
}
printf("%d", ans);
return 0;
}
#5104. YAOI Summer Round #4 (Div 2) D. 矩阵
得分率远低于预期。
首先,我们会意识到令所有的 (max(x,y)modmin(x,y)=1) 不劣,下面再进行讨论。
对于 (20\%) 的数据,通过计算器容易构造出一组解。(从左到右从上到下构造即可)
对于 (40\%) 的数据,考虑像国际象棋那样黑白染色,染黑色的取互不相同的质数,染白色的取周围 (4) 个黑色格子中数的乘积即可。
对于 (100\%) 的数据,实际上与 (40\%) 类似,只需要把质数的相对位置进行微调,让大的两个和小的两个配在一起就好了。
复杂度 (O(n^2))。
#include <bits/stdc++.h>
#define Re register
using namespace std;
typedef long long ll;
const int N = 505, TOT = 10000005;
int n, tot, p[TOT];
bool vis[TOT];
ll a[N][N];
inline void prework(int mxn) {
vis[1] = 1;
for (Re int i = 2; i <= mxn; i++) {
if (!vis[i])
p[++tot] = i;
for (Re int j = 1; j <= tot && i * p[j] <= mxn; j++) {
vis[i * p[j]] = 1;
if (i % p[j] == 0)
break;
}
}
}
inline ll gcd(ll x, ll y) { return y == 0 ? x : gcd(y, x % y); }
inline ll lcm(ll x, ll y) {
if (x == 0 || y == 0)
return x + y;
return 1ll * x / gcd(x, y) * y;
}
int main() {
prework(10000000);
scanf("%d", &n);
if (n == 2) {
printf("4 7
");
printf("23 10
");
return 0;
}
for (Re int i = 1; i <= n; i++) {
for (Re int j = 1; j <= n; j++) {
if ((i + j) % 2 == 0) {
a[i][j] = 1ll * p[(i + j) / 2] * p[n + (n + 1) / 2 + (i - j) / 2];
}
}
}
for (Re int i = 1; i <= n; i++) {
for (Re int j = 1; j <= n; j++) {
if ((i + j) % 2 == 1) {
a[i][j] = lcm(lcm(a[i - 1][j], a[i][j - 1]), lcm(a[i + 1][j], a[i][j + 1])) + 1;
}
}
}
for (Re int i = 1; i <= n; i++) {
for (Re int j = 1; j <= n; j++) {
printf("%lld ", a[i][j]);
}
puts("");
}
return 0;
}