CF 984 D XOR-Pyramid

题意:

  • 定义长度为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 xor a = 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;
}

猜你喜欢

转载自blog.csdn.net/xyyxyyx/article/details/81205103