Loading

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 -

难度: ⭐⭐

题目大意

现有一个长度为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;
}
posted @ 2024-04-14 18:30  mostimali  阅读(23)  评论(0编辑  收藏  举报