链接:https://ac.nowcoder.com/acm/contest/81/E?&headNav=www
来源:牛客网

题目:


题目描述

给一个1-base数组{a},有N次操作,每次操作会使一个位置无效。一个区间的权值定义为这个区间里选出一些数的异或和的最大值。求在每次操作前,所有不包含无效位置的区间的权值的最大值。 

输入描述:

第一行读入一个正整数(1 <= n <= 105)

第二行读入n个正整数,第i个表示a[i](0<= a[i] <= 109)

第三行读入n个正整数,第i个表示x[i]即第i次操作的位置,保证x[i]互不相同。

输出描述:

输出n行答案

示例1

输入

10
169 816 709 896 58 490 97 254 99 796 
4 2 3 10 5 6 1 8 9 7

输出

1023
994
994
994
490
490
254
254
99
97

解题思路:


每次某个位置无效后都会将这n个数划分成多个段,有些数要从线性基中删除。从线性基中删除数字不易,但是可以对线性基进行合并,所以需要逆操作,即对于样例,处理时无效位置的顺序是7 9 8 1  6 5 10 3 2 4

同时这道题还需要用到并查集的思想,用l[pos],r[pos]记录当前pos左右两边“位置无效”所能达到的最远下标

拿样例来说明:(位置无效说明该位置上数被删除了)

i 1 2 3 4 5 6 7 8 9 10
a[i] 169 816 709 896 58 490 97 254 99 796
程序处理时删除的顺序 4 9 8 10 6 5 1 3 2 7

“程序处理时删除的顺序”越大实际上在正序执行题目要求时越先被删除。

left=l[pos],right=r[pos],每次都让【left】下的线性基和【pos+1】下的线性基进行合并,得到新的【left】下的线性基(并查集思想)

并记录【left】下的线性基的最大异或值,和上一次求得的其他区间的最大异或值比较,取最大值。

最后倒数输出这些记录的结果。

参照代码模拟一下吧,语言表达能力受限,表述得不太明白。

这道题,max_base不可以设成32,可以设成31,30,1e9是30位的

ac代码:


#include <bits/stdc++.h>
using namespace std;
const int maxn=100005;
const int max_base=30;
typedef long long ll;
int a[maxn],x[maxn],ans[maxn],l[maxn],r[maxn],vis[maxn];
int xian[maxn][max_base+3]={0};//存线性基,用到并查集的思想
int n;
void solve()
{
    //倒序删除
    for(int i=1;i<=n;i++)
    {
        int pos=x[n-i+1],left=pos,right=pos;//倒序
        vis[pos]=1;
        if(l[pos-1]) left=l[pos-1];
        if(r[pos+1]) right=r[pos+1];
        l[pos]=left,r[pos]=right;
        r[left]=right,l[right]=left;//[l[pos],r[pos]]表示pos所在的最大的区间,该区间内的数表示的位置都是无效的

        int p=a[pos];//当前位置pos无效,pos位置上对应的数

        for(int j=max_base;j>=0;j--)
        {
            if(p>>j&1)
            {
                if(xian[left][j])
                    p^=xian[left][j];
                else
                {
                    xian[left][j]=p;
                    break;
                }
            }
        }
        //线性基合并
        if(vis[pos+1])//对于全0的线性基无需合并
        {
             for(int k=max_base;k>=0;k--)
             {
                 for(int j=max_base;j>=0;j--)
                 {
                     if(xian[pos+1][k]>>j&1)//将pos+1对应的线性基中j位为1的数合并到left对应的线性基中
                     {
                         if(xian[left][j])
                             xian[pos+1][k]^=xian[left][j];
                         else
                         {
                             xian[left][j]=xian[pos+1][k];
                             break;
                         }
                     }
                 }
             }
        }
        ans[i]=0;
        for(int j=max_base;j>=0;j--)
            if(xian[left][j]) ans[i]=max(ans[i],ans[i]^xian[left][j]);
        //当某个位置无效时可以分割出多个段,上面的循环只是计算出了left所对应的区间段的最大异或值,还要和其他段的异或值值比较
        ans[i]=max(ans[i],ans[i-1]);
    }
}
int main() {
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        scanf("%d",&x[i]);
    solve();
    for(int i=n;i>=1;i--)
        printf("%d\n",ans[i]);
    return 0;
}