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