[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;
}

浙公网安备 33010602011771号