双指针
- 基础算法中概括的双指针首先不是指针,是用两个数来代替坐标进行增加或减少的操作,从而达到确定区间的作用。
- 双指针的使用前提是,有一个区间 [l, r] 满足条件,并且他的任意一个子数组都满足条件。
题解:因数个数为偶数 <=>(等价于) 非完全平方数
我遇到的问题是完全平方数sq的取值应该是j * j,列举的是前200个完全平方数,因为非平方数明显比平方数要多,采用正难则反的原则。如果是与平方数异或为0的那么证明是枚举到这个平方数了,拿总数减去这个当前平方数。
#include <bits/stdc++.h>
const int N = 1e5 + 9;
using namespace std;
int a[N], pre[N], cnt[N];
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
pre[i] = pre[i - 1] ^ a[i]; //数组的异或和前缀
cnt[0] = 1;
//总数ans减去不满足的数量就是答案
int ans = n * (n + 1) / 2; //
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= 200; j++) { //从0开始:0的因数个数也是奇数个。
int sq = j * j;
ans -= cnt[pre[i] ^ sq]; //如果pre[i]和sq异或的结果为0,就是相等,那么pre[i]是平方和,那么减去1。
//因为上面让cnt[0]=1了。
}
cnt[pre[i]]++;//至于这段代码,若pre[i]^sq = pre[j]等价于pre[i]^pre[j] = sq,说明原数组的i-j的元素异或和是平方和。
//因为我们相当于对数组只进行一重循环,所以每遍历完一个前缀异或和都要放到cnt数组里面,cnt数组的作用就是是否减1。
//这个操作就相当于二重循环,非常经典
}
cout << ans;
return 0;
}
思维拓展
异或是一个很强大的功能,| a - b | <= a ^ b <= a + b,在任何条件下是成立的;
在a ^ b ^ c = a + b + c成立的前提下,a ^ b = a + b; b ^ c = b + c;
a + b >= a | b >= max(a,b);
0 <= a & b <= min(a, b);