USACO Dec 2024 Bronze 比赛记录
\(\rm{T1}\)
思路
观察链式舍入的性质
随便找几个特殊的数字
- 一位数 无
- 两位数 \(45, 46, 47, 48, 49\)
- 三位数 \(445 \sim 499\)
- 四位数 \(4445 \sim 4999\)
- 五位数 \(44445 \sim 49999\)
- \(\cdots\)
容易发现对于 \(N\) , 我们首先计算出 \(N\) 的位数, 然后再飞开讨论计算即可
唯一特殊的是对于 \(N\) 的位数, 我们先判断他在 \(44 \cdots 45 \sim 49 \cdots 99\) 的那个位置, 再特殊计算
实现
#include <bits/stdc++.h>
#define int long long
int T;
int n;
int pow10[10] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
signed main()
{
scanf("%lld", &T);
while (T--) {
scanf("%d", &n);
int Ans = 0;
int len = 0, ncopy = n;
while (ncopy) len++, ncopy /= 10;
int i, sum;
for (i = 2, sum = 5; i <= len - 1; i++, sum *= 10, sum += 5)
Ans += sum;
int C = 0;
for (int i = len - 1; i >= 0; i--) C += 4 * pow10[i];
C++;
if (len >= 2 && n < C || len < 2) {
printf("%lld\n", Ans);
} else if (len >= 2 && n >= C && n <= 5 * pow10[len - 1] - 1) {
printf("%lld\n", Ans + n - C + 1);
} else {
printf("%lld\n", Ans + sum);
}
}
return 0;
}
注意开 \(\rm{long \ long}\)
也是最抽象的一集, 交了 \(4\) 发
\(\rm{T2}\)
思路
题意大概在说, 对于每个删除操作, 都要求你 求出 现在有多少个空余的整行
转化题意, 问题变成给定 \(Q \leq 2 \cdot 10^5\) 个三元串 \((x, y, z)\) , 求第 \(i\) 个询问给定后, 对于 \((x, y, z)\) 中的任意两个, 另外一个覆盖了 \(0 \sim n - 1\) 的数量
答案肯定是单调不降的, 我们直接在每次询问的时候维护一下当前的这一个删除操作带来的答案改变即可
具体的, 我们用三个 \(\rm{Hash}\) 表记录前两个, 后两个, 前后各一个选择的情况, 然后每次只要有新答案, 更新即可
所以我为什么会觉得有 \(N\) 维
实现
#include <bits/stdc++.h>
int N, Q;
std::map<std::pair<int, int>, int> Delete12, Delete13, Delete23; // 最多也就 3e6 的空间
int main()
{
scanf("%d %d", &N, &Q);
int Ans = 0;
for (int i = 1; i <= Q; i++) {
int x, y, z;
scanf("%d %d %d", &x, &y, &z);
if (++Delete12[{x, y}] == N) Ans++;
if (++Delete13[{x, z}] == N) Ans++;
if (++Delete23[{y, z}] == N) Ans++;
printf("%d\n", Ans);
}
return 0;
}
\(\rm{T3}\)
思路
好神秘的题面
好困难, 我不会是智障吧
先不考虑变化的字符, 我们可以 \(\mathcal{O} (N)\) 的计算出当前字符串中, 含有所有长度为 \(3\) 的串串的个数
那么考虑变化字符之后怎么办呢, 我们可以朴素一点, 枚举字符串每一个位置的变化, 然后统计一下当前每种长度为 \(3\) 的串串的个数, 符合要求的就输出
时间复杂度 \(\mathcal{O} (\omega^3 N)\) , 其中 \(\omega = 26\)
这样子过不去, 考虑优化
容易发现在之前的计算中, 我们大可不必枚举所有长度为 \(3\) 的串, 显然的, 我们只有在产生了新的 moo 时才处理, 那么时间复杂度大概是 \(\mathcal{O} (\omega ^ 2 N)\) , 应该可以通过, 有些极限
还有一个实现上的问题, 如何判断更改后对长度为 \(3\) 的串串的出现次数
对于中间的串串, 我们考虑形如 abcde 这样的串串, 修改 c , 那么 abc, bcd, cde 出现次数会被影响
交上去发现还是 \(\rm{TLE}\) 了, 考虑优化
改了一下计数, 应该过了
实现
#include <bits/stdc++.h>
int N, F;
std::string S;
std::map <std::string, int> ApTinS; // 原串中的出现次数
std::map <std::string, bool> Can; // 是否可以作为 moo
void calc()
{
for (int i = 0; i < 26; i++) {
for (int j = 0; j < 26; j++) {
if (i == j) continue;
char a = char(i + 'a'), b = char(j + 'a');
std::string Str;
Str += a, Str += b, Str += b;
if (ApTinS[Str] >= F) Can[Str] = true;
}
}
}
int main()
{
scanf("%d %d", &N, &F);
std::cin >> S; S = ' ' + S;
for (int i = 1; i <= N - 2; i++) {
std::string nowS = S.substr(i, 3);
ApTinS[nowS]++;
}
calc();
/*枚举修改的位置*/
for (int i = 1; i <= N; i++) {
for (int j = 0; j < 26; j++) {
if (S[i] == j + 'a') continue;
std::string Sp = S; Sp[i] = j + 'a';
std::string S3[4], S3p[4]; int cnt = 0;
for (int k = std::max(1, i - 2); k <= i; k++)
S3[++cnt] = S.substr(k, 3);
cnt = 0;
for (int k = std::max(1, i - 2); k <= i; k++)
S3p[++cnt] = Sp.substr(k, 3);
bool meaningful = false;
for (int k = 1; k <= cnt; k++)
if (S3p[k][0] != S3p[k][1] && S3p[k][1] == S3p[k][2]) meaningful = true;
if (!meaningful) continue;
for (int k = 1; k <= cnt; k++)
ApTinS[S3[k]]--, ApTinS[S3p[k]]++;
for (int k = 1; k <= cnt; k++) {
if (S3p[k][0] != S3p[k][1] && S3p[k][1] == S3p[k][2])
if (ApTinS[S3p[k]] >= F) Can[S3p[k]] = true;
}
for (int k = 1; k <= cnt; k++)
ApTinS[S3[k]]++, ApTinS[S3p[k]]--;
}
}
int Ans = 0;
for (int i = 0; i < 26; i++) {
for (int j = 0; j < 26; j++) {
if (i == j) continue;
char a = char(i + 'a'), b = char(j + 'a');
std::string Str;
Str += a, Str += b, Str += b;
if (Can[Str]) Ans++;
}
}
printf("%d\n", Ans);
for (int i = 0; i < 26; i++) {
for (int j = 0; j < 26; j++) {
if (i == j) continue;
char a = char(i + 'a'), b = char(j + 'a');
std::string Str;
Str += a, Str += b, Str += b;
if (Can[Str]) std::cout << Str << '\n';
}
}
return 0;
}

浙公网安备 33010602011771号