题意:
- 定义长度为m的序列b,其值为f(b)=f( b[1}^b[2],b[2]^b[3],..,b[m-1]^b[m]) m>1
Q次询问,每次询问[L,R]内价值最大的子区间. n<=5e3, 0<=a[i]<2^30 Q<=1e5.
思路:
对于一个长度m的序列b,某个元素i对答案的贡献恰好是杨辉三角的c[m, i],
即f(b) = c[m, 1]个b[1] xor c[m, 2]个b[2] xor ⋯ ⋯ xor c[m, m]个b[m]因为 a a xor = 0,所以c[i, j]不需要求出确切值,只需要mod 2就行了。然后根据百度的说法,不进位的二进制加法就是异或,所以转移方程变成了c[i, j] = c[i-1, j-1] xor c[i-1, j]。
再所以,假设g[l, r, i]表示在[l, r]这个序列里的第i个数的贡献,则g[l, r, i] = g[l, r-1, i]^g[l+1, r, i-1]。因为g[l, r, i]里a[i]的系数是c[i, j],而g[l, r-1, i]系数是c[i-1, j],g[l+1, r, i-1]系数是c[i-1, j-1],所以通过异或运算就可以得到答案。
因为一个数列中每一个数都可以通过g[l, r, i] = g[l, r-1, i]^g[l+1, r, i-1]递推,那最后对于序列的递推式就是:
f[l, r] = f[l, r-1] xor f[l+1, r],写起来及其简单啊,然而很难想到。(实际上是按这种思路很难想到,其实如果一开始就把整个序列逐步变成一个数的过程写下来,就可以瞬间得出递推式)再说一个错误思路:手膜好几组数据之后,发现好像长度为奇数的序列只取头尾各一个异或,偶数长度序列取头尾各两个,看似肥肠正确,然而到第7层就出错了。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 5010;
int n, a[N], f[N][N], g[N][N], q, x, y;
main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]), g[i][i] = f[i][i] = a[i];
for (int i = 1; i < n; i++)
for (int l = 1; l <= n-i; l++){
int r = l+i;
f[l][r] = f[l][r-1]^f[l+1][r];
g[l][r] = max(f[l][r], max(g[l][r-1], g[l+1][r]));
}
scanf("%d", &q);
while (q--){
scanf("%d%d", &x, &y);
printf("%d\n", g[x][y]);
}
return 0;
}