Maybe something will change|

Semorius

园龄:1年9个月粉丝:4关注:10

📂题解
🔖CF
2023-07-09 08:48阅读: 4评论: 0推荐: 0

CF1747D

CF1747D

题意

给定一个长度为 n 的序列 a 和若干询问区间,问能否通过若干次操作使询问区间均变为 0。一次操作可以选择一个长度为奇数的区间并将这个区间区间赋值为这个区间的异或和。

思路

考虑这样一个性质:每次操作后不会改变这个区间的异或值。

证明:设当前操作的区间为 al,al+1,...,ar。设 i=lrai=v,操作后序列变为 rl+1v 连接而成,由于 rl+11(mod2),所以 i=lrv=v,即操作后区间异或值不改变。

根据这个性质开始分类讨论。

如果询问区间每个数初始时均为 0,操作次数为 0

如果一个区间的异或值不为 0,无论如何操作,都无法使该区间每个数都变为 0

如果询问区间长度为奇数,对整个区间操作 1 次即可。

最后剩下区间长度为偶数的情况。发现并不是所有的情况都有解,比如 3,0,0,3 这个情况,虽然它的区间异或和为 0,但无论先操作前三个或后三个,都会使整个序列变为 3,3,3,3,然后无法操作。

所以,当询问区间长为偶数时,当且经当这个区间存在一个长为奇数的异或和为 0 的前缀时有解。

考虑如何快速判断是否存在长为奇数的前缀异或和为 0。先预处理出原序列的前缀异或和,记一个 nxtx 表示从下标为 x 的位置开始往后第一个下标与当前位奇偶性不同且其前缀异或和与当前位相等的位置,每次询问判断 nxtL1 是否在询问区间内,如果在即有解。

但我赛时以为偶数情况有解时一定要操作两次,导致WA on test2了无数发。其实有些情况只需要操作一次。比如 1,2,3,0 这种情况,只需要操作一次前三位就可以变为 0,0,0,0。更普遍的,只要区间两端点有一个值为 0,就可以通过忽略这个 0 使区间长变为奇数,再整体操作 1 次即可。

Code

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll SIZE = 200005;
const ll mod = 998244353;
ll n, T;
ll a[SIZE], sum[SIZE];
ll cnt[SIZE];
ll nx[SIZE];
map<ll, ll> mp[2];
inline ll rd(){
ll f = 1, x = 0;
char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return f*x;
}
int main(){
n = rd(); T = rd();
for(ll i = 1; i <= n; i++) {
a[i] = rd(), sum[i] = (sum[i-1] ^ a[i]);
cnt[i] = cnt[i-1] + a[i];
}
for(ll i = n; i >= 0; i--){
nx[i] = mp[(i%2)^1][sum[i]];
if(nx[i] == 0) nx[i] = n+1;
mp[i%2][sum[i]] = i;
}
while(T--){
ll l = rd(), r = rd();
if(sum[r] ^ sum[l-1]) printf("-1\n");
else if(cnt[r] - cnt[l-1] == 0) printf("0\n");
else if((r-l+1)&1) printf("1\n");
else if(nx[l-1] <= r){
if(a[l] == 0 || a[r] == 0) printf("1\n");
else printf("2\n");
}
else printf("-1\n");
}
return 0;
}

本文作者:Semorius

本文链接:https://www.cnblogs.com/Semorius/p/17538289.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Semorius  阅读(4)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起