P8763 [蓝桥杯 2021 国 ABC] 异或变换 题解
本蒟蒻的第一篇题解。
让我们来看一点不一样的解法——
(其实也不是那么不一样)
题解区大部分都是用周期的方式完成的。原因很简单:
- 直接模拟会超时。主要原因就是
t 太大。 - 周期非常明显。周期也很短,大概在
n 左右。这样就可以直接把很大的t 给消掉。
所以,大多数人都选择用周期去做。
但是,当我拿到题目的时候,我第一个想到的是有点类似离散化的思想。如果在一个 0 之后有很多个连续字符都是 1,那么我只需要把第一个字符变为 1 即可,没有必要一一枚举每一个字符。,我只需要存储每一个需要变动的字符,然后一一变动就好了。
所以,这道题我选择了队列。
怎么变动呢?这里直接将队首元素取反即可,因为本题异或只与原来的数值有关,而它能进入队列说明它与周围的原数值一定满足需要变化的条件。
接下来,我们需要判断变化的字符需不需要继续变化。
不仅是这样,我们还要判断它后面的字符是否因为它的变化也需要变化。而它后面的字符可能因为一开始符合不变化的条件,所以它有可能根本没入队。因此需要额外的判断。
然后就是空间问题。我在调试代码的时候,发现空间不优化的话会炸掉。参照 SPFA 的优化经验,需要记录元素是否在队列中。
AC 代码
#include<bits/stdc++.h>
using namespace std;
int n,t,len = 1;
int num[10005];
queue<pair<int,int> > que;
char tp;
int main(){
scanf("%d%d",&n,&t);
scanf("\n");
scanf("%c",&tp);
num[1] = tp-'0';
for(int i = 2; i <= n; i++){
scanf("%c",&tp);
num[i] = tp-'0';
if(num[i]^num[i-1] != num[i]){
que.push({1,i});
}
}
while(len <= n){
len *= 2;
}
t %= len;
while(que.size()){
int a = que.front().first;
int b = que.front().second;
que.pop();
if(a > t){
break;
}
num[b] = num[b] ? 0 : 1;
if(num[b]^num[b-1] != num[b]){
que.push({a+1,b});
}
if(num[b]^num[b+1] != num[b+1]){
que.push({a,b+1});
}
}
for(int i = 1; i <= n; i++){
printf("%d",num[i]);
}
return 0;
}