A. Sasha and a Bit of Relax
source: codeforces
tag: dp
implementation
这道题的重点在于
- 对亦或位运算(⊕或^)的理解,位运算中,亦或 = 加(不进位) = 减(不借位)。因此变换含有多个⊕符号的式子时,可以把他当成减号/加号来处理。
- 对于亦或等式变换的能力
- 对于pref的理解(pref_i就是从a_1一直亦或到a_i的一长串变量相亦或得到的结果)
思路
首先从题目得到:
a_l ⊕ a_(l+1) ⊕ …… ⊕ a_mid = a_(mid+1) ⊕ a_(mid+2) ⊕ …… ⊕ a_r
左边和右边称为一对funny pair,我们的任务是找出输入数组里面有多少对这样的funny pair。
由亦或运算的特性,两边相等,亦或出来的结果为0 (eg. 1⊕1=0),可以得到:
a_l ⊕ a_(l+1) ⊕ …… ⊕ a_r = 0
现在的任务变成了:数组中有多少对数能够取(l,r), 使得r-l+1为偶数且满足上述从l到r亦或和=0的式子。
我们定义
pref_i = a_1 ⊕ a_2 ⊕ …… ⊕ a_i
通过 pref_r ⊕ pref_l-1
, 我们可以得到a_l ⊕ a_(l+1) ⊕ …… ⊕ a_r
, 因此,只要满足pref_r ⊕ pref_l-1 = 0
, 那么即可以得到我们一开始所求的pref_r = pref_l-1
。
现在的任务变成了:数组中有多少对,能够使r-l+1为偶数且满足pref_r = pref_l-1
实现
先定义变量,n代表输入数组的长度,a[]代表输入数组,x表示pref(下标和当前亦或的a[i]的i相同),cnt是一个二维数组,第一维表示奇偶,第二维表示曾经达到过异或和(pref)为x的次数,res表示统计的funny pair对数(只有在cnt[i%2][x]里面有东西时才会增加,否则只是加0)。
-
为了满足r-l+1为偶数,我们采取奇偶分治的策略:l-1和r的奇偶必定一样,否则l到r总共不为偶数个,不足成pair。因为为了满足l-1到r为偶数,我们认为l-1和r的奇偶性一致。
-
我们定义了一个二维数组,第一维长度为2,[0]、[1]分别代表l-1/r为偶、奇。第二维我们取长度为(1<<20)+3 ,都全置为0,代表pref的结果。
-
如果输入数组中,有相同第一维(l-1/r奇偶性相同,满足偶数项),并且pref结果也相同的不同位置,那么这两个位置之间(不包括[l-1]但包括[r])能够组成一对funny pair,res需要自加上代表他们重合的数字,即cnt[i%2][x]的值(本来的值应该为0,只有在[l-1]给这个cnt[i%2][x]加1之后,在[r]才可能加上非零的值)
-
代码
var n=readline(); // 读取数组长度n
var a=readline().split(' '); // 读取数组
a=a.slice(0,n); // 将数组长度设为n
var cnt= []; // 初始化cnt数组
cnt.length=2; // cnt数组第一维长度为2
for(var k=0; k<2; k++){
cnt[k] = []; // cnt数组第二维
cnt[k].length=(1<<20)+3; // 第二维长度
cnt[k].fill(0); // 全部置0
}
cnt[1][0]=1; // 给奇数数组的那一边的第一个pref设为1,因为pref_1=1;
var res=0; // res变量用来存储变量
var x=0; // 当前i的pref=0
for(var i=0; i<n; ++i){
x^=a[i]; // 和数组的第i个元素a[i]的亦或,即前i个元素的亦或和
res+=cnt[i%2][x];
// x=pref_i,即从0到n所有pref_i的值
// 如果其中有两个相等的话(即pref_r=pref_l-1),就会被加到res中去
++cnt[i%2][x]; // 和这个pref的值相等次数+1
}
print(res);
复制代码
在codeforces的js引擎中,readline()代表输入,print()代表打印。
- 测试
Input 5 1 2 3 4 5 Output 1 Expected Answer 1