链接:https://www.nowcoder.com/acm/contest/81/E
来源:牛客网
题目描述
给一个1-base数组{a},有N次操作,每次操作会使一个位置无效。一个区间的权值定义为这个区间里选出一些数的异或和的最大值。求在每次操作前,所有不包含无效位置的区间的权值的最大值。
输入描述:
第一行读入一个正整数(1 <= n <= 105)
第二行读入n个正整数,第i个表示a[i](0<= a[i] <= 109)
第三行读入n个正整数,第i个表示x[i]即第i次操作的位置,保证x[i]互不相同。
解题思路:线性基维护异或最大和,然后用并查集维护区间连通性,我们把所有查询从后往前考虑,就相当于不断地插入数字,然后就不断的合并区间(线性基)即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct XXJ
{
ll d[61];
bool insert(ll val)
{
for (int i = 60; i >= 0; i--)
if (val & (1LL << i))
{
if (!d[i])
{
d[i] = val;
break;
}
val ^= d[i];
}
return val > 0;
}
ll query_max()
{
ll ret = 0;
for (int i = 60; i >= 0; i--)
if ((ret ^ d[i]) > ret)
ret ^= d[i];
return ret;
}
ll query_min()
{
for (int i = 0; i <= 60; i++)
if (d[i])
return d[i];
return 0;
}
};
void merge(XXJ &x, XXJ &y, XXJ &z)
{
XXJ tmp;
for (int i = 60; i >= 0; i--)
tmp.d[i] = y.d[i];
for (int i = 60; i >= 0; i--)
if (z.d[i])
{
ll tp = z.d[i];
for (int i = 60; i >= 0; i--)
if (tp & (1LL << i))
if (!tmp.d[i])
{
tmp.d[i] = tp;
break;
}
else
tp ^= tmp.d[i];
}
for (int i = 60; i >= 0; i--)
x.d[i] = tmp.d[i];
}
int a[100005];
int q[100005];
XXJ s[100005];
int vis[100005];
int pre[100005];
int find(int x)
{
return pre[x] == x ? x : pre[x] = find(pre[x]);
}
int main()
{
for (int i = 0; i <= 100000; i++)
pre[i] = i;
int N;
scanf("%d", &N);
for (int i = 0; i < N; i++)
{
scanf("%d", &a[i]);
s[i + 1].insert(a[i]);
}
for (int i = 0; i < N; i++)
{
scanf("%d", &q[i]);
}
ll ans = -1000;
for (int i = N - 1; i >= 0; i--)
{
if (vis[q[i] - 1] == 1)//是否与左边联通,是的话就合并
{
int fx = find(q[i] - 1);
int fy = find(q[i]);
pre[fy] = fx;
vis[q[i]] = 1;
merge(s[fx], s[fy], s[fx]);
}
if (vis[q[i] + 1] == 1)//右边
{
int fx = find(q[i] + 1);
int fy = find(q[i]);
pre[fy] = fx;
vis[q[i]] = 1;
merge(s[fx], s[fy], s[fx]);
}
int fx = find(q[i]);
vis[q[i]] = 1;
ans = max(ans, s[fx].query_max());//ans同时记录了上次的最大值,这样就能顺便把其他区间的查询了
q[i] = ans;
}
for (int i = 0; i < N; i++)
printf("%d\n", q[i]);
return 0;
}