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\) 的转移就是:

\[\begin{aligned} f_{i,len_i} & = 26 \\ f_{i,j} & = 25 \times \sum_{k=1}^{i-1} f_{k,j-len_{i-k}} \end{aligned} \]

其中 \(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;
}
posted @ 2022-04-24 10:23  Suzt_ilymtics  阅读(193)  评论(1编辑  收藏  举报