Educational Codeforces Round 174 (Rated for Div. 2) (A~D)
前言
将近一年没有更新博客了,突发奇想更新一下。(主要原因是好久没打CF了...)
长时间没写题,场上脑子转不过来,代码写的又长又臭。
A. Was there an Array? (签到)
题目问是否存在这种构造,顺便拉个样例 \(222333\) 发现两个 \(1\) 之间至少存在两个 \(0\), 检查一下即可
void solve(){
cin >> n;
for(int i = 1; i <= n - 2; i++) {
cin >> a[i];
}
int flag = 0; // 表示前面是否有1
int cnt = 0; // 表示离上个1之间有多少0
for(int i = 1; i <= n - 2; i ++) {
if(flag == 0 && a[i] == 1) {
flag = 1;
cnt = 0;
}
if(flag) {
if(a[i] == 0) {
cnt ++;
}
else {
if(cnt == 1) { // 可能存在 11 的情况,这里不能 cnt<=1
cout << "NO\n";
return;
}
cnt = 0;
}
}
}
cout << "YES\n";
}
B. Set of Strangers (签到)
题意是每次操作选取一些不相邻的相同颜色格子,涂上任意一种颜色,求最终所有格子颜色相同需要几次操作。
一个显然的结论是一个颜色最多用两次操作就能全部选取到。
判断是不是有相同颜色的格子相邻,是的话就要操作两次,否则一次即可。
最后贪心一下。
void solve(){
cin >> n >> m;
cnt[0] = cnt[1] = cnt[2] = 0; // 表示需要一次操作与两次操作的颜色数量
for(int i = 1; i <= n * m ;i++) vis[i] = 0; // vis表示该同种颜色是否相邻,1表示出现过但不相邻,2表示有相邻情况
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
cin >> a[i][j];
if(vis[a[i][j]] == 0) {
vis[a[i][j]] = 1;
}
else if(a[i - 1][j] == a[i][j] || a[i][j - 1] == a[i][j]) { //判断相邻
vis[a[i][j]] = 2;
}
}
}
for(int i = 1; i <= n * m; i++) {
cnt[vis[i]]++;
}
if(cnt[2] == 0) { // 贪心取答案
cout << cnt[1] - 1 << endl;
}
else {
cout << (cnt[2] - 1) * 2 + cnt[1] << endl;
}
}
C. Beautiful Sequence (简单dp)
题意说的很复杂,又是条件,又是举例,实际上都没什么用...
由于元素只有 \({1,2,3}\) ,所以只有形如 \([1,2,3],[1,2,2,3]\)以\(1,3\)为两端,中间都是\(2\)的可行。求这种子序列的数量。
暴力思想就是枚举\(1,3\),计算其中有多少个 \(2\) 。根据组合数学中间有 \(x\) 个 \(2\) 的贡献是 \(2^x - 1\)。
想法是计算到目前为止,对于所有可能左端点\(1\)的方案数之和进行转移。
每个\(1\)都能产生\(2^x-1\)的贡献,其中 \(x\) 我们不方便保存,但\(-1\)容易,因此保存用\(cnt\)数组记录\(1\)的个数。
虽然 \(x\) 不能保存,但是会发现每新增一个\(2\),都会使前面每一段的\(x+1\)。也就是贡献翻倍。
转移随便分类一下,见代码。
void solve(){
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
int ans = 0;
for(int i = 1; i <= n; i++) {
if(a[i] == 1) {
pre[i] = pre[i - 1] + 1;
cnt[i] = cnt[i - 1] + 1;
pre[i] %= mod;
}
else if(a[i] == 2) {
pre[i] = pre[i - 1] * 2;
cnt[i] = cnt[i - 1];
pre[i] %= mod;
}
else {
pre[i] = pre[i - 1];
cnt[i] = cnt[i - 1];
int x = (pre[i] - cnt[i] + mod) % mod;
ans = (ans + x) % mod;;
}
}
cout << ans << endl;
}
D. Palindrome Shuffle (贪心)
纯贪心删删改改莫名其妙就过了。
赛后看了眼标签,二分,哈希一个都没用上。
题意是重新排列一个连续子串使字符串回文,求最短子串长度。
1.两端已经对称的就不用改了,把两端忽略掉。
2.剩下的如果分成左右两半,如果字母数量一样,则可以只修改一侧使其对称
3.如果字母数量不一样,表示子串需要覆盖两侧,枚举从左到右和从右到左哪个更优。
题目给的样例有大部分情况,模拟一下即可。
#include<bits/stdc++.h>
using namespace std;
#define SHOJIG ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl '\n'
#define x1 xxxx
#define y1 yyyy
#define int long long
#define lowbit(x) (x & (-x))
typedef long long ll;
const int N = 1e6 + 9, inf = 0x3f3f3f3f;
int n, m, k;
string s;
int cnt[30], tmp[30],lef[30], rig[30]; // 史山代码
int check1() { // 如果一侧的字母都能在另一部分找到,就是可能的答案
for(int i = 0; i <= 25; i++) {
if(lef[i] < tmp[i] - lef[i]) return 0;
}
return 1;
}
int check2() {
for(int i = 0; i <= 25; i++) {
if(rig[i] < tmp[i] - rig[i]) return 0;
}
return 1;
}
void solve(){
cin >> s;
for(int i = 0; i <= 25; i++) cnt[i] = tmp[i] = lef[i] = rig[i] = 0;
int n = s.length();
s = " " + s;
int l = 1, r = n;
while(l < r && s[l] == s[r]) {
l++;
r--;
}
if(l >= r) {
cout << 0 << endl;
return;
}
// case 1:
int l1 = n / 2, r1 = n / 2 + 1, flag = 1;
for(int i = l ; i <= l1; i++) { // 左右两半字母数量是否相同
cnt[s[i] - 'a']++;
}
for(int i = r1; i <= r; i++) {
cnt[s[i] - 'a']--;
}
for(int i = 0; i <= 25; i++) {
if(cnt[i] != 0) {
flag = 0;
}
}
if(flag == 1) { // 相同的情况从中心再开始判断回文,尽可能减少需要改动的长度
while(s[l1] == s[r1]) {
l1--;
r1++;
}
cout << l1 - l + 1 << endl; // 任取一边修改
return;
}
else {
for(int i = l; i <= r; i++) { // 记录中间总共字母数量
tmp[s[i] - 'a']++;
}
// left 从左到右覆盖
int ans = r - l + 1, res = 0; // res记录长度
for(int i = l; i <= r; i++) {
res++;
lef[s[i] - 'a']++;
if(check1()) {
ans = min(res, ans);
}
}
// right 从右到左覆盖
res = 0;
for(int i = r; i >= l; i--) {
res++;
rig[s[i] - 'a']++;
if(check2()) {
ans = min(res, ans);
}
}
cout << ans << endl;
}
}
signed main(void){
SHOJIG
//init();
int _ = 1;
cin >> _;
for(int i = 1;i <= _; i++) solve();
}

浙公网安备 33010602011771号