[USACO22OPEN] COW Operations S 题解

周末刷题的时候看见这题,做完感觉太巧妙了,写篇题解纪念一下。

题目传送门

题意概述

给定一个只包含"C" "O" "W"三个英文大写字母的字符串,可以进行两项操作:

  • 选择两个相邻而且相同的字符并将其删除。
  • 选择一个字母将其变成另外两个字母,排列随意。

给定Q个查询,每个查询询问一个子串是否可以通过上面两种操作变成一个字母"C"。

Solution

关注一手操作。

操作一似乎没什么值得关注的,看操作二。选择两个字母将其变成另外一个字母,合理联想一下:

"OW" -> "CWW" -> "C" ; "WO" -> "COO" -> "C" ;

"CW" -> "OWW" -> "O" ; "WC" -> "OCC" -> "O" ;

"OC" -> "WCC" -> "W" ; "CO" -> "WOO" -> "W" ;

容易发现两个相邻字母最后转化成的字母一定相同。上面还有一些情况没列举出来,但结果一定是一样的。

看回操作一,两个相同字母最后会删除......你回想起了一个trick:异或!

上面这些东东不正好对应异或么?

令"C"为1,"O"为2,"W"为3。

那么显然这个东西满足:异或之后的结果可以对应回刚刚我们尝试出的转化之后的结果。试试不就知道了

因为异或满足结合律,所以每个子串无论怎么合并结果都是一样的。

而至于题目里的要求最后变成字母"C",也就是这个子串里的异或最终结果应当是1。

那么我们直接一个前缀异或和快速求出结果即可。

给不懂前缀异或的童鞋们科普一下,前缀异或和是利用异或x^x=0的性质,用类似前缀和的实现来快速求出区间内异或和的一个trick。
说详细一点,就是利用异或的自逆性(x^y^x)=y的特性,将前缀和的pre[r]-pre[l-1]改成了pre[r]^pre[l-1]以此舍去l-1往前的部分。
看代码可以辅助理解。

AC code

#include<bits/stdc++.h> 
#define int long long
using namespace std;
const int N=200005;
string s;
int q,pre[N];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>s;
	for(int i=0;i<s.size();i++){
        //映射
		if(s[i]=='C')pre[i+1]=1;
		if(s[i]=='O')pre[i+1]=2;
		if(s[i]=='W')pre[i+1]=3;
        //求前缀异或和
		pre[i+1]^=pre[i];
	}
	cin>>q;
	while(q--){
		int l,r;
		cin>>l>>r;
		if((pre[r]^pre[l-1])==1)cout<<"Y";
		else cout<<"N";
	}
	return 0;
}
posted @ 2026-07-01 13:55  fireclouds_11  阅读(10)  评论(0)    收藏  举报