题目笔记——P10069 [CCO 2023] Flip it and Stick it

赛后发现错因竟是 -1 写成 1,1 和 0 写反,+= 写成 =。

题意

判断能否将一个字符串S通过任意次翻转子串后使得 \(S\) 不包含 \(T\) 的长度很小作为子串,如果可以,。

仔细观察可以发现 \(T\) 的长度很小,最多只有3个单位长度,反正不会做,来分类讨论一下吧。

\(|T|=1\)

\(S\) 字符串只会被旋转,其中的字符不会改变,如果一开始 \(S\) 中就有 \(T\) 了,那么 \(T\) 就永远是 \(S\) 的子串,输出 -1,否则一开始 \(S\) 就不包含 \(T\) 作为子串,输出 0。

\(|T|=2\)

\(T_{1}\neq T_{2}\)

如果 \(T_{1}\neq T_{2}\)(\(T_{1}\) 指 \(T\) 的第 \(1\) 位),就要保证在 \(S\) 中 \(T_{1}\) 后面没有任何 \(T_{2}\)、\(T_{2}\) 前面没有任何 \(T_{1}\),就是要使 \(S\) 变成一段 \(T_{2}\) 加一段 \(T_{1}\),每一次翻转可以使一个前面有 \(T_{1}\) 的 \(T_{2}\) 翻转到最前面,所以就是找 \(S\) 中 \(T\) 的个数。

\(T_{1}=T_{2}\)

如果 \(T_{1}=T_{2}\),就要保证在 \(S\) 中每个 \(T_{1}\) 之间都有至少一个另一个数间隔,所以就要先判断 \(S\) 中 \(1\) 和 \(0\) 的个数,如果 \(S\) 中 \(T_{1}\) 的个数大于另一个数的个数加一,每一次翻转可以使一个 \(T\) 之间加一个数,所以就是找 \(S\) 中 \(T\) 的个数。

\(|T|=3\)

\(T_{1}\neq T_{3}\)

我们发现每一次翻转可以消除 \(S\) 中的一个 \(T\),类似上面的\(|T|=2,T_{1}\neq T_{2}\)。

\(T=010\) 或 \(T=101\)

我们发现此时 \(T\) 翻转不会改变,且一次可以消除 \(S\) 中的两个 \(T\),所以答案就是 \(\lceil\frac{num_T}{2}\rceil\),其中 \(num_T\) 是指 \(S\) 中 \(T\) 的个数。

\(T=000\) 或 \(T=111\)

类似于 \(|T|=2,T_{1}=T_{2}\),\(S\) 中的每个 \(T\) 都要被分隔开,且一次只能消除一次,我们可以看每一个 \(T_{1}\oplus1\)(即 \(T_{1}\) 取反)左右 \(T_{1}\) 连续的个数,对于每个连续个数大于 \(2\) 的连续段中如果有可以一次性移走两个的连续段,可以优先选择将其移至相邻 \(T_{1}\oplus1\) 之间没有 \(T_{1}\) 的段。

代码(码风过史,勿喷)

#include <bits/stdc++.h>
using namespace std;
#define int long long
int num, num1, num0, cnt1, cnt2, wt1, wt2;
string s, t;
vector <int> e; 

signed main(){
	cin >> s >> t;
	if(s.find(t) >= s.size()){
		cout << 0;
		return 0;
	}
	if(t.size() == 1){
		cout << -1;
		return 0;
	}
	if(t.size() == 2){
		if(t[0] != t[1]){
			for(int i = 1; i < s.size(); i++){
				if(s[i - 1] == t[0] && s[i] == t[1]) num ++;
			}
			cout << num;
			return 0;
		}
		if(t == "00"){
			for(int i = 0; i < s.size(); i++){
				if(s[i] == '1') num1 ++;
				else num0 ++;
			}
			if(num1 < num0 - 1){
				cout << -1;
				return 0;
			}
			for(int i = 1; i < s.size(); i++){
				if(s[i - 1] == '0' && s[i] == '0') num ++;
			}
			cout << num;
			return 0;
		}
		if(t == "11"){
			for(int i = 0; i < s.size(); i++){
				if(s[i] == '1') num1 ++;
				else num0 ++;
			}
			if(num0 < num1 - 1){
				cout << -1;
				return 0;
			}
			for(int i = 1; i < s.size(); i++){
				if(s[i - 1] == '1' && s[i] == '1') num ++;
			}
			cout << num;
			return 0;
		}
		return 0;
	}
	if(t[0] != t[2]){
		for(int i = 2; i < s.size(); i++){
			if(s[i - 2] == t[0] && s[i - 1] == t[1] && s[i] == t[2]) num ++;
		}
		cout << num;
		return 0;
	}
	if(t == "000"){
		for(int i = 0; i < s.size(); i++){
			if(s[i] == '1'){
				e.push_back(num);
				num = 0;
				num1 ++;
			}
			else{
				num ++;
				num0 ++;
			}
		}
		e.push_back(num);
		if(2 * (num1 + 1) < num0){
			cout << -1;
			return 0;
		}
		cnt1 = 0, cnt2 = 0, wt1 = 0, wt2 = 0;
		for(int i : e){
			if(i == 0) cnt2 ++;
			if(i == 1) cnt1 ++;
			if(i > 2){
				wt2 += (i - 2) / 2;
				wt1 += i % 2;
			}
		}
		if(wt2 <= cnt2) cout << wt1 + wt2;
		else cout << wt1 + cnt2 + (wt2 - cnt2 << 1);
		return 0;
	}
	if(t == "111"){
		for(int i = 0; i < s.size(); i++){
			if(s[i] == '1'){
				num ++;
				num1 ++;
			}
			else{
				e.push_back(num);
				num = 0;
				num0 ++;
			}
		}
		e.push_back(num);
		if(2 * (num0 + 1) < num1){
			cout << -1;
			return 0;
		}
		cnt1 = 0, cnt2 = 0, wt1 = 0, wt2 = 0;
		for(int i : e){
			if(i == 0) cnt2 ++;
			if(i == 1) cnt1 ++;
			if(i > 2){
				wt2 += (i - 2) / 2;
				wt1 += i % 2;
			}
		}
		if(wt2 <= cnt2) cout << wt1 + wt2;
		else cout << wt1 + cnt2 + (wt2 - cnt2 << 1);
		return 0;
	}
	for(int i = 2; i < s.size(); i++){
		if(s[i - 2] == t[0] && s[i - 1] == t[1] && s[i] == t[2]) num ++;
	}
	cout << (num + 1) / 2;
	return 0;
}
抄代码的一眼就看的出来
posted @ 2026-10-27 16:27  show__code  阅读(3)  评论(0)    收藏  举报