链接: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;
}