AtCoder Beginner Contest 346
B - Piano
难度: ⭐⭐
题目大意
现有S为无限重复字符串"wbwbwwbwbwbw"形成的字符串。请问S中是否存在由W次出现的'w'和B次出现的'b'组成的子字符串T;
解题思路
字符串T显然可以由S的一段后缀 + 若干个S + S的一段前缀组成; 但是, 这个题的W和B都最大为100; 也就是说, 如果存在, 那么一定会在S重复200次以内的时候得到, 所以暴力就可以了;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int n, m, res = inf;
string s = " wbwbwwbwbwbw";
string s1;
signed main(){
IOS;
cin >> w >> b;
for (int i = 0; i <= 20; i++) {
for (int j = 0; j < (int)s.length(); j++) {
s1.push_back(s[j]);
}
}
for (int i = 0; i < (int)s1.size(); i++) {
int cntw = 0, cntb = 0;
for (int j = i; j < (int)s1.size(); j++) {
if (w == cntw && b == cntb) {
cout << "Yes\n";
return 0;
}
if (s1[j] == 'b') cntb++;
else cntw++;
}
}
cout << "No";
return 0;
}
C - CΣ
难度: ⭐⭐
题目大意
现有一个长度为n的序列A, 给定一个正整数k, 请问1 - k中未出现在序列A中的数字之和;
解题思路
反向思路, 我们可以先求1 - k中出现在序列A中的数字之和; 对此先把序列A中小于等于k的部分进行排序去重求和; 最后用k * (k + 1) / 2减去它就可以了;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int n, m, res = inf;
int p[N], s[N];
vector<int> v;
signed main(){
IOS;
cin >> n >> m;
v.push_back(0);
for(int i = 0; i < n; i++){
int a;
cin >> a;
v.push_back(a);
}
sort(v.begin(), v.end());
v.erase(unique(v.begin() , v.end()), v.end());
for(int i = 1; i < v.size(); i++){
s[i] = s[i - 1] + v[i];
}
int l = upper_bound(v.begin(), v.end(), m) - v.begin();
int x = m * (m + 1) / 2;
x -= s[l - 1];
cout << x;
return 0;
}
D - Gomamayo Sequence
难度: ⭐⭐⭐
题目大意
对于一个长度为n的01字符串S, 只有S中只有一处相邻的两个数字相同的时候才是一个好字符串, eg 0100101; 并且对于1 ~ n的字符S[i], 我们可以花费C[i]将其反转, 0变成1, 1变成0; 请问把字符串S变成一个好字符串的最小代价是多少;
解题思路
可以用dp来解决, dp1[i][j]表示得到长度为i且情况为j的好字符串的最小代价; dp2[i][j]表示得到长度为i且情况为j的坏字符串的最小代价; 这里的坏字符串表示01交替的字符串, eg: 01010101; 情况j意思是, 如果j为1, 则说明第i个和第i - 1个字符不同, j为0则为相同;
这样我们就可以根据S[i]和S[i - 1]来进行状态转移; 具体转移方程可以看代码;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int n, m, res = inf;
int dp1[N][2], dp2[N][2], p[N];
string s;
signed main(){
IOS;
string s;
cin >> n >> s;
s = ' ' + s;
for(int i = 1; i <= n; i++){
cin >> p[i];
}
dp2[1][1] = p[1];
dp1[1][1] = dp1[1][0] = inf;
for(int i = 2; i <= n; i++){
if(s[i] == s[i - 1]){
dp1[i][0] = min(dp2[i - 1][0], dp1[i - 1][1]);
dp1[i][1] = min(dp2[i - 1][1], dp1[i - 1][0]) + p[i];
dp2[i][0] = dp2[i - 1][1];
dp2[i][1] = dp2[i - 1][0] + p[i];
}
else{
dp1[i][0] = min(dp2[i - 1][1], dp1[i - 1][0]);
dp1[i][1] = min(dp2[i - 1][0], dp1[i - 1][1]) + p[i];
dp2[i][0] = dp2[i - 1][0];
dp2[i][1] = dp2[i - 1][1] + p[i];
}
}
cout << min(dp1[n][1], dp1[n][0]);
return 0;
}
E - Paint
难度: ⭐⭐⭐
题目大意
现有一个n x m网格, 初始均为0; 现在进行m次操作(a, b, c), 如果a为1, 则把第b行都变成数字c, 如果a为2, 则把第b列都变成数字c; 最后输出网格中有多少种颜色, 每个颜色都有多少个格子;
解题思路
因为后操作会把之前的操作覆盖, 所以我们可以倒着处理, 每次处理完一个操作后更新当前还可以染色的长和宽即可;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int dx[] = {1, 0, -1, 0, 1, 1, -1, -1};
int dy[] = {0, 1, 0, -1, 1, -1, 1, -1};
int n, m, k;
struct node{
int a, b, c;
};
node p[N];
bool row[N], col[N];
map<int, int> mp;
signed main(){
IOS;
int sum = 0;
cin >> n >> m >> k;
for(int i = 1; i <= k; i++){
int a, b, c;
cin >> a >> b >> c;
p[i] = {a, b, c};
}
mp[0] = n * m;
for(int i = k; i >= 1; i--){
if(p[i].a == 1){
if(row[p[i].b] || m == 0) continue;
mp[p[i].c] += m;
sum += m;
n--;
row[p[i].b] = true;
}
else{
if(col[p[i].b] || n == 0) continue;
mp[p[i].c] += n;
sum += n;
m--;
col[p[i].b] = true;
}
}
mp[0] -= sum;
if(mp[0] == 0) mp.erase(0);
cout << mp.size() << endl;
for(auto t : mp){
cout << t.first << ' ' << t.second << endl;
}
return 0;
}
F - SSttrriinngg in StringString
难度: ⭐⭐⭐⭐
题目大意
对于一个长度为n的字符串S, 函数f(S, k)表示把S重复k次后得到的字符串, g(S, k)表示把S中的每个字符都重复k次后得到的字符串; ed: S = "abc", 那么f(S, 2) = "abcabc", g(S, 3)= "aaabbbccc"; 当然, f(S, 0)和g(S, 0)都是空字符串; 给你一个正整数N和字符串S, T; 求最大的非负整数k,使得g(T, k)是f(S, N)的子序列; 注意,根据定义,g(T, 0)总是f(S, N)的子序列。
解题思路
为了方便, 我们开始两个数组p[N][26], f[N][26]; p[i][j] = x表示S的前i个字符中字母j的个数, f[i][j] = x表示S的前x个字符中恰好有i个字母j, 并且x尽可能小; 因为k有单调性, 所以可以用二分来求;
在check函数中, 先设定两个指标num和pos代表当前进度, 表示已经经过了num个字符S, 并且此时正位于第num + 1个S的pos位置; 我们遍历字符串T, 对于T[i], 我们需要在当前进度的基础上往后找N个字符T[i], 而且是从S的一段后缀 + 若干个S + S的一段前缀中找; 中间的若干个S可以对p[S.size() - 1][T[i]]取余来得到; 接下来就可以分类讨论, 先看从pos到最后中T[i]的数量够不够用, 如果不够用就num + 1, pos = 0, 再从前缀开始找;
注意这里有个坑点, 如果N对p[S.size() - 1][T[i]]取余为0, 那么不能直接加上N / p[S.size() - 1][T[i]], 而是加上(N / p[S.size() - 1][T[i]]) - 1, 然后再从后缀前缀中再找p[S.size() - 1][T[i]]个T[i], 这样才能把空间利用最大化;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int dx[] = {1, 0, -1, 0, 1, 1, -1, -1};
int dy[] = {0, 1, 0, -1, 1, -1, 1, -1};
int n, m, res;
string s, t;
int p[N][26], f[N][26];
bool check(int u){
if(u == 0) return true;
int num = 0;
int pos = 0;
for(int i = 1; i < t.size(); i++){
int ap = t[i] - 'a';
if(p[s.size() - 1][ap] == 0) return false;
int a = u / p[s.size() - 1][ap];
int b = u % p[s.size() - 1][ap];
if(b == 0){
num += a - 1;
b = p[s.size() - 1][ap];
}
else num += a;
int re = p[s.size() - 1][ap] - p[pos][ap];
if(re >= b){
pos = f[b + p[pos][ap]][ap];
}
else{
num++;
pos = f[b + p[pos][ap] - p[s.size() - 1][ap]][ap];
}
if(num > n || (num == n && pos > 0)){
return false;
}
}
return true;
}
signed main(){
IOS;
cin >> n >> s >> t;
s = ' ' + s;
t = ' ' + t;
for(int i = 0; i < 26; i++){
for(int j = 1; j < s.size(); j++){
if(s[j] == 'a' + i){
p[j][i] = p[j - 1][i] + 1;
}
else p[j][i] = p[j - 1][i];
}
}
for(int i = 1; i < s.size(); i++){
int num = p[i][s[i] - 'a'];
f[num][s[i] - 'a'] = i;
}
int l = 0, r = inf;
while(l < r){
int mid = l + r + 1 >> 1;
if(check(mid)){
l = mid;
}
else r = mid - 1;
}
cout << l;
return 0;
}