线性基性质:
一个数组集合中,一些元素相互异或 能得到原数组中的 任意元素
线性基的应用:
- 一个数组中,寻找最大异或和 or 最小异或和
- 一个数组中,第K小异或和
- 看一个数字是否能被线性基 异或 得到
- [L , R] 区间内,最大异或和
线性基应用的对应解法
- 最大异或和
- 第K小异或和
/* 引自————https://blog.csdn.net/Jasmineaha/article/details/100015215 */ using namespace std; typedef long long LL; const int Maxn = 1e6 + 5; const int Inf = 1e9 + 7; const int Over = 62; int N , Q; typedef long long LL; LL a[Maxn], f[70]; int n, m, cnt = 0; void guass() { for(int i = Over; i >= 0; i--) f[i] = 0LL; for(int i = 1; i <= n; i++) { for(int j = Over; j >= 0; j--) { if((a[i] >> j) & 1) { if(!f[j]) { f[j] = a[i]; break; } a[i] ^= f[j]; } } } for(int i = Over; i >= 0; i--) { for(int j = i + 1; j <= Over; j++) if((f[j] >> i) & 1) f[j] ^= f[i]; // 只有f[i]的第i位为1 } cnt = 0; for(int i = 0; i <= Over; i++) if(f[i]) f[cnt++] = f[i]; } LL getKth(LL k) { if(cnt < n) { // 说明原序列可以异或出0 if(k == 1) return 0; // 直接返回最小值0 else k--; // 去掉0这个最小值后的第K小 } if(k >= (1LL << cnt)) return -1; LL ans = 0LL; for(int i = 0; i <= Over; i++) { if((k >> i) & 1) ans ^= f[i]; } return ans; } int main() { int T;scanf(" %d",&T); int cas = 0 while(T--){ printf("Case #%d:\n", ++cas); scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%lld", a + i); guass(); scanf("%d", &m); while(m--) { LL k; scanf("%lld", &k); printf("%lld\n", getKth(k)); } } return 0; }
- 能否被线性基异或得到——将此数字插入线性基,能插入则说明不能得到,不能插入则能得到
- [L, R] 区间内的最大异或和——HDU6579
/*维护维护每个点的前缀线性基,线性基里将靠右的数字尽可能放高位,就是存一个额外存一个位置 p,表示这个位上的数的位置,从高位到低位扫,如果当前位置大于这个位上的位置那么交换,然后就得到了一个靠右的数字尽可能在高位的线性基 然后对于询问 [l,r] 在 r 的前缀线性基里找,只在位置大于等于 l 的位更新答案 ————————注解 & 代码 引自https://www.cnblogs.com/xay5421/p/11228086.html */ using namespace std; typedef long long LL; const int Maxn = 1e6 + 5; const int Inf = 1e9 + 7; const int Over = 32; int N , Q; int a[Maxn]; int lastans; int sum[Maxn][Over]; int pos[Maxn][Over]; int tot; void add(int num){ ++tot; for(int i = 0 ; i < Over ; ++i){ sum[tot][i] = sum[tot-1][i]; pos[tot][i] = pos[tot-1][i]; } int now = tot; for(int i = Over - 1 ; i >= 0 ; --i){ if(num & (1<<i)){ if(!sum[tot][i]){ sum[tot][i] = num; pos[tot][i] = now; break; } if(now > pos[tot][i]){ swap(now , pos[tot][i]); swap(num , sum[tot][i]); } num ^= sum[tot][i]; } } } int query(int l , int r){ int ans = 0; for(int i = Over - 1 ; i >= 0 ; --i){ if(sum[r][i] && pos[r][i] >= l){ ans = max(ans , ans ^ sum[r][i]); } } return ans; } int main() { int T; scanf(" %d",&T); while(T--){ lastans = tot = 0; scanf(" %d %d",&N,&Q); for(int i = 1 ; i <= N ; ++i){ scanf(" %d",&a[i]); add(a[i]); } while(Q--){ int op , l , r; scanf(" %d",&op); if(!op){ scanf(" %d %d",&l,&r); l = (l ^ lastans) % N + 1; r = (r ^ lastans) % N + 1; if(l > r) swap(l , r); lastans = query(l , r); printf("%d\n",lastans); } else { scanf(" %d",&r); add(r ^ lastans); N++; } } } }