AtCoder Beginner Contest 249 赛时记录
赛时只切了 4 题,2022 开年来的第一次,输麻了/ll
掉大分
安慰一下自己,常在河边走,哪有不掉分~
A - Jogging
直接按照题目要求的模拟一下,看看谁的得分高就可以。
tnnd 题面一开始错了,WA 了一发,比着题面和样例看了半天才看出来/px
B - Perfect String
随便拿个桶记录下就好了。
C - Just K
\(n\) 很小,直接 \(2^n\) 枚举,然后 \(O(26n)\) 求一下价值,对价值取个 \(\max\) 就好了。
signed main() {
int n = read(), K = read();
for(int i = 1; i <= n; ++i) {
cin >> s + 1;
int len = strlen(s + 1);
for(int j = 1; j <= len; ++j) {
cnt[i][s[j] - 'a'] ++;
}
}
int Max = 0;
for(int S = 0; S < (1 << n); ++S) {
int ans = 0;
for(int i = 0; i < 26; ++i) {
int res = 0;
for(int j = 1; j <= n; ++j) {
if(!((S >> j - 1) & 1)) continue;
res += cnt[j][i];
}
if(res == K) ans ++;
}
// cout << S << " " << ans << "\n";
Max = max(Max, ans);
}
cout << Max << "\n";
return 0;
}
D - Index Trio
发现 \(a_i \le 2 \times 10^5\),果断把 \(a_i\) 扔到值域上统计出现次数。
然后暴力枚举一下 \(i\) 和 \(i \times j\) 就好了,复杂度是喜闻乐见的调和级数 \(\mathcal O(V \ln V)\)。
注意我们并不需要考虑去重的问题。
signed main() {
n = read();
for(int i = 1; i <= n; ++i) cnt[read()] ++;
int M = 200000;
for(int i = 1; i <= M; ++i)
for(int j = i; j <= M; j += i)
ans += cnt[i] * cnt[j] * cnt[j / i];
cout << ans << "\n";
return 0;
}
E - RLE
重新看了一下 E 题,随便冲了一发就过了。
设 \(f_{i,j}\) 表示填完前 \(i\) 个位置,缩减后长度为 \(j\) 的方案数。
显然有一个 \(n^3\) 的转移就是:
其中 \(len_{i}\) 表示一个长度为 \(i\) 的字符串缩减后的长度。这个可以预处理一下。
因为 \(len_i\) 的值只有四种取值(\(2,3,4,5\)),而转移的时候这些又是连续的。
我们考虑处理一个前缀和 \(s_{i,j} = \sum_{k=1}^{i} f_{k,j}\)。
那么转移后面的求和部分可以利用这个前缀和快速算出。
我们只需要暴力写一下四种取值的转移即可。
总复杂度是 \(\mathcal O(n^2)\) 的。
下面是代码,代码去掉了不重要的部分。
#define int long long
const int MAXN = 3e3 + 100;
const int INF = 1e9 + 7;
int n, mod, ans = 0;
int f[MAXN][MAXN]; // f[i][j] 表示前 i 个位置,长度为 j 的方案数
int sum[MAXN][MAXN]; // sum[i][j] = \sum_{k=1}^{i} f[k][j]
int len[MAXN];
int Mod(int x) {
if(x < 0) return (x % mod + mod) % mod;
return x % mod;
}
int Get(int i, int j, int L, int R) {
if(j - len[L] < 0) return 0;
return Mod(sum[max(0ll, i - L)][j - len[L]] - sum[max(0ll, i - R)][j - len[L]]) * 25 % mod;
}
signed main() {
n = read(), mod = read();
for(int i = 1; i < 10; ++i) len[i] = 2;
for(int i = 10; i < 100; ++i) len[i] = 3;
for(int i = 100; i < 1000; ++i) len[i] = 4;
for(int i = 1000; i <= 3000; ++i) len[i] = 5;
f[0][0] = 1;
for(int i = 1; i <= n; ++i) {
f[i][len[i]] = 26;
for(int j = 1; j <= n; ++j) {
f[i][j] = Mod(f[i][j] + Get(i, j, 1, 10));
f[i][j] = Mod(f[i][j] + Get(i, j, 10, 100));
f[i][j] = Mod(f[i][j] + Get(i, j, 100, 1000));
f[i][j] = Mod(f[i][j] + Get(i, j, 1000, 3001));
sum[i][j] = Mod(sum[i - 1][j] + f[i][j]);
}
}
for(int j = 1; j < n; ++j) ans = Mod(ans + f[n][j]);
cout << ans << "\n";
return 0;
}
F - Ignore Operations
因为还没调出来,先口胡一下做法。 调出来了,思路没有问题。
就是倒着枚举,遇到 \(t_i=1\) 的时候,看一下从这里到最后获得的价值最大是多少,对答案去一个 \(\max\)。过去之后要用一次跳来消除这个位置的影响。
动态维护一个 \(sum\),每遇到一个 \(t_i = 2\),如果 \(y_2 \ge 0\),那么可以不用跳。否则的话,把它扔进一个堆里,如果堆里的元素个数 \(>K\),取出最小的加进 \(sum\) 中。
中途如果扫过 \(t_i=1\) 后 \(K<0\),那么可以直接 break
掉。
最后直接输出答案即可。
#define int long long
const int MAXN = 4e5 + 10;
const int INF = 1e18 + 7;
const int mod = 998244353;
int n, K, ans = - INF, sum = 0;
int a[MAXN], b[MAXN];
priority_queue<int, vector<int>, less<int> > Q;
signed main() {
n = read(), K = read();
for(int i = 1; i <= n; ++i) a[i] = read(), b[i] = read();
ans = 0; int Cnt = 0;
for(int i = 1; i <= n; ++i) {
if(a[i] == 1) ans = b[i];
else ans += b[i], ++Cnt;
}
a[0] = 1, b[0] = 0;
for(int i = n; i >= 0; --i) {
if(a[i] == 1) {
ans = max(ans, b[i] + sum);
--K;
if(K < 0) break;
} else {
if(b[i] >= 0) sum += b[i];
else Q.push(b[i]);
}
while(!Q.empty() && Q.size() > K) {
sum += Q.top(), Q.pop();
}
}
cout << ans << "\n";
return 0;
}