http://www.javashuo.com/article/p-dimugxuz-e.htmlphp
最近的幾場多校出現了好幾回線性基的題目,,會想起以前在嘗試西安區域賽的一道區間異或和最大的問題時,當時由於異或的性質知道這道題確定用線段樹來維護區間的最值,可是不知道用什麼來處理異或和最大,,即便後來知道了能夠用線性基來處理,看了一些博客也由於感受太難收藏到書籤就再也沒看過,,,因而這幾天,花了差很少四、5天的時間,大概看懂了這部分的內容,感受這只是一種專門處理異或問題的一個工具,光這個工具沒什麼意思,,如今的不少題目都是用線性基套各類東西,,好比說很常見的線段樹(大多都是詢問)、樹鏈剖分(也就是樹上路徑的異或問題,主要是求LCA來維護)、簡單圖以及像杭電第一場的那題同樣貪心魔改線性基板子等等,,不可能單純的只是用線性基板子來求一個什麼最值,K值,並交等等性質,下面是我這幾天學習線性基的簡單的一個學習過程的記錄。html
關於線性基,雖然看起來這三個字很高深,,可是等大體瞭解以後,就會發現,這只是一個簡單的數學工具,基礎知識就是學過的線性代數 (雖然早就忘記了) 。node
拋開線性代數,我我的的理解就是 線性基就是一個用來表示給定集合的一個最少的數的集合, 用線性基這個集合,能夠表示它所 張成 的一個集合,對於咱們遇到的大多數題目來講,就是用一個最少的數的集合 \(lb\) 經過 異或 的形式能夠表示數組(集) \(a\) 。ios
咱們能夠用 \(n\) 個 \(2^i\) 這樣不一樣的二進制數組 \(a_i\) 的異或來表示全部 \([0 ...2^n-1]\) 的任意一個數,例如: \([01], [10]\) 可一個表示 \(0, 1, 2, 3\) 。 但若是去掉 \(a_i\) 中的一些數,顯然能表示的數集就減小了些,,反過來想,對於任意一個數集,咱們均可以找到這樣一個數集的子集 \(a_i\) ,\(a_i\) 中的任意數的異或和能夠表示這個數集中的每個數,這樣咱們至關於對原數集進行了壓縮,用一個小的集合表示出來了,,並且顯然他的最大大小就是數在二進制表示的位數,c++
能夠這樣表示的緣由是由於對於一個線性基,他能夠看做是一個 向量組 ,這些向量間是線性無關的,也就是說任意一個向量都不能夠經過其餘的向量表示,也就是線性基中的每個數都不能夠經過其餘數的異或獲得,,這樣的一個向量組的線性組合能夠 張成 一個線性空間,,,(具體的更加詳細的數學知識能夠看這裏) 或者 維基算法
在ACM中大多數的線性基的做用就是維護一段數的異或的各類性質,例如最值、K值、一個數 \(x\) 可否能夠被這些數的異或和表示、線性基的交併等等。這只是一個工具,主要是和其餘知識點的結合。數組
不知道是這個知識點不那麼重要仍是怎麼的,,不像其餘的算法,網絡上找到的關於線性基好的資料不多不多,,尤爲是板子,,沒有註釋,,新手 (我) 一開始根本看不懂,,只能硬啃前面的數學推導,,而後轉化成代碼,,,最後本身在借鑑別人的基礎上弄出了一份本身的板子,,,(怕不是過幾天就忘了寫的什麼)網絡
根據定義,線性集就是一個數的集合,並且長度通常題中會給,,ll就是64,int就是32等等,,因此他就是一個數組就好了,,,數據結構
static const int maxbase = 35; ll a[maxbase + 1];
線性基的最基本的操做就是遍歷一個數集,,而後挑出其中的線性基,這個過程就是將一個數 \(t\) 插入到線性基中,,對於當前線性基 \(a\) ,若是它中的元素和 \(t\) 線性無關,那麼 \(t\) 就是一個基底,,把他插入到線性基中,,根據定義,,若是發現是線性相關的,,那麼就說明如今的線性基 \(a\) 能夠表示這個數,,一頓操做以後,,\(t\) 必定變成了0,,(用這個能夠判斷是否一個數能被線性基表示,,,dom
插入的本質就是一個維護線性基矩陣的過程,,有兩種維護的形式,,一種就是無論插入的元素對其餘元素的影響,,維護一個上三角的線性基矩陣,,這樣的時間複雜讀低一些; 另外一種就是利用高斯消元,,對於一個能夠插入的元素,先用下面的行來消本身,而後用本身消上面幾行 ,,這樣能夠保證插入一個元素後,線性基的矩陣只有插入的那一行對應的那一位是1,,其餘的都是零,,也就是一個對角矩陣。
bool insert(ll t) { //暴力插入一個數,維護的是一個上三角型的線性基矩陣,時間複雜度低,當待插入元素能插入時,返回true for(int i = maxbase; i >= 0; --i) { if(t & (1ll << i)) { if(!a[i]) { a[i] = t; break; } t ^= a[i]; } } if(t == 0)flag = true; return t; } bool query(ll t) { // 詢問t是否能夠被當前線性基表示,不插入 if(t > queryMax())return false; if(t == 0)return true; for(int i = maxbase; i >= 0; --i) { if(t & (1ll << i)) { if(!a[i]) { return false; } t ^= a[i]; } } return true; } void Insert(ll t) { //插入一個線性基,利用高斯消元法維護一個對角矩陣 for(int i = maxbase; i >= 0; --i) { if(t >> i & 1) { if(a[i])t ^= a[i]; else { a[i] = t; for(int j = i - 1; j >= 0; --j)if(a[j] && (a[i] >> j & 1))a[i] ^= a[j]; for(int j = i + 1; j <= maxbase; ++j)if(a[j] >> j & 1)a[j] ^= a[i]; break; } } } }
//詢問最值 ll queryMax() { ll ret = 0; for(int i = maxbase; i >= 0; --i) if((ret ^ a[i]) > ret) ret ^= a[i]; return ret; } ll queryMin() { for(int i = 0; i <= maxbase; ++i) if(a[i]) return a[i]; return 0; }
並好說,直接暴力加到一個線性基中就好了,,反正不能插入的會在插入過程當中變成0,不用管
交是牛客第四場多校才遇到的,,(整個線性基也是多校才遇到) ,(交的板子只找到一個看得懂的),,,交的大體思路是這樣的 (瞎猜ing):
LinearBasis merge(const LinearBasis &l1, const LinearBasis &l2) { // 獲得兩個線性基的並 LinearBasis ret = l1; for(int i = maxbase; i >= 0; --i) if(l2.a[i]) ret.insert(l2.a[i]); return ret; } LinearBasis intersection(const LinearBasis &l1, const LinearBasis &l2) { //獲得兩個線性基的交 LinearBasis all, ret, full; ret.clear(); for(int i = maxbase; i >= 0; --i) { all.a[i] = l1.a[i]; full.a[i] = 1ll << i; } for(int i = maxbase; i >= 0; --i) { if(l2.a[i]) { ll v = l2.a[i], k = 0; bool flag = true; for(int j = maxbase; j >= 0; --j) { if(v & (1ll << j)) { if(all.a[j]) { v ^= all.a[j]; k ^= full.a[j]; } else { // l2's basis is not in l1's; flag = false; all.a[j] = v; full.a[j] = k; break; } } } if(flag) { ll v = 0; // get intersection by k; for(int j = maxbase; j >= 0; --j) { if(k & (1ll << j)) { v ^= l1.a[j]; } } ret.insert(v); //save ans } } } return ret; }
(留坑,,,尚未作題。。。
struct LinearBasis { static const int maxbase = 35; bool flag = false; ll a[maxbase + 1]; LinearBasis() { // memset(a, 0, sizeof a); } LinearBasis(ll *x, int n) { LinearBasis(); build(x, n); } void build(ll *x, int n) { for(int i = 1; i <= n; ++i) insert(x[i]); } void clear() { memset(a, 0, sizeof a); } bool insert(ll t) { //暴力插入一個數,維護的是一個上三角型的線性基矩陣,時間複雜度低,當待插入元素能插入時,返回true for(int i = maxbase; i >= 0; --i) { if(t & (1ll << i)) { if(!a[i]) { a[i] = t; break; } t ^= a[i]; } } if(t == 0)flag = true; return t; } bool query(ll t) { // 詢問t是否能夠被當前線性基表示,不插入 if(t > queryMax())return false; if(t == 0)return true; for(int i = maxbase; i >= 0; --i) { if(t & (1ll << i)) { if(!a[i]) { return false; } t ^= a[i]; } } return true; } void Insert(ll t) { //插入一個線性基,利用高斯消元法維護一個對角矩陣 for(int i = maxbase; i >= 0; --i) { if(t >> i & 1) { if(a[i])t ^= a[i]; else { a[i] = t; for(int j = i - 1; j >= 0; --j)if(a[j] && (a[i] >> j & 1))a[i] ^= a[j]; for(int j = i + 1; j <= maxbase; ++j)if(a[j] >> j & 1)a[j] ^= a[i]; break; } } } } LinearBasis merge(const LinearBasis &l1, const LinearBasis &l2) { // 獲得兩個線性基的並 LinearBasis ret = l1; for(int i = maxbase; i >= 0; --i) if(l2.a[i]) ret.insert(l2.a[i]); return ret; } LinearBasis intersection(const LinearBasis &l1, const LinearBasis &l2) { //獲得兩個線性基的交 LinearBasis all, ret, full; ret.clear(); for(int i = maxbase; i >= 0; --i) { all.a[i] = l1.a[i]; full.a[i] = 1ll << i; } for(int i = maxbase; i >= 0; --i) { if(l2.a[i]) { ll v = l2.a[i], k = 0; bool flag = true; for(int j = maxbase; j >= 0; --j) { if(v & (1ll << j)) { if(all.a[j]) { v ^= all.a[j]; k ^= full.a[j]; } else { // l2's basis is not in l1's; flag = false; all.a[j] = v; full.a[j] = k; break; } } } if(flag) { ll v = 0; // get intersection by k; for(int j = maxbase; j >= 0; --j) { if(k & (1ll << j)) { v ^= l1.a[j]; } } ret.insert(v); //save ans } } } return ret; } //詢問最值 ll queryMax() { ll ret = 0; for(int i = maxbase; i >= 0; --i) if((ret ^ a[i]) > ret) ret ^= a[i]; return ret; } ll queryMin() { for(int i = 0; i <= maxbase; ++i) if(a[i]) return a[i]; return 0; } };
熟悉下板子,,敲一下就能夠了,,
對於每一個數集中的數,有一個第二權值,要保證選的數集中的數異或和不爲零的狀況下權值最大,,只要選權值從大到小且下標異或和不爲零的元素的貢獻就能夠了,
// luogu-judger-enable-o2 #include <bits/stdc++.h> // #include <iostream> // #include <cstdio> // #include <cstdlib> // #include <string.h> // #include <vector> // #include <algorithm> // #include <set> // #include <vector> // #include <cmath> // #include <queue> // #include <stack> // #include <ctime> // #include <random> #define aaa cout<<233<<endl; #define endl '\n' #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef long double ld; // mt19937 rnd(time(0)); const int inf = 0x3f3f3f3f;//1061109567 > 1e9 const ll linf = 0x3f3f3f3f3f3f3f; const double eps = 1e-5; const double pi = 3.14159265358979; const int maxn = 1e4 + 5; const int maxm = 1e4 + 5; const int mod = 1e9 + 7; struct LinearBasis { static const int maxbase = 62; ll a[maxbase + 1]; LinearBasis() { memset(a, 0, sizeof a); } LinearBasis(ll *x, int n) { LinearBasis(); build(x, n); } void build(ll *x, int n) { for(int i = 1; i <= n; ++i) insert(x[i]); } bool insert(ll t) { for(int i = maxbase; i >= 0; --i) { if(t & (1ll << i)) { if(!a[i]) { a[i] = t; break; } t ^= a[i]; } } return t; } void Insert(ll t) { for(int i = maxbase; i >= 0; --i) { if(t >> i & 1) { if(a[i])t ^= a[i]; else { a[i] = t; for(int j = i - 1; j >= 0; --j)if(a[j] && (a[i] >> j & 1))a[i] ^= a[j]; for(int j = i + 1; j <= maxbase; ++j)if(a[j] >> j & 1)a[j] ^= a[i]; break; } } } } LinearBasis merge(const LinearBasis &l1, const LinearBasis &l2) { LinearBasis ret = l1; for(int i = maxbase; i >= 0; --i) if(l2.a[i]) ret.insert(l2.a[i]); return ret; } ll queryMax() { ll ret = 0; for(int i = maxbase; i >= 0; --i) if((ret ^ a[i]) > ret) ret ^= a[i]; return ret; } ll queryMin() { for(int i = 0; i <= maxbase; ++i) if(a[i]) return a[i]; return 0; } }; pair<int, ll> p[maxn]; int main() { // double pp = clock(); // freopen("233.in", "r", stdin); // freopen("233.out", "w", stdout); ios_base::sync_with_stdio(0); cin.tie(0);cout.tie(0); int n; cin >> n; for(int i = 1; i <= n; ++i)cin >> p[i].second >> p[i].first; sort(p + 1, p + 1 + n, greater<pair<int, ll>>()); LinearBasis l; int ans = 0; for(int i = 1; i <= n; ++i)if(l.insert(p[i].second))ans += p[i].first; cout << ans << endl; // cout << endl << (clock() - pp) / CLOCKS_PER_SEC << endl; return 0; }
題目問一些 \(01\) 序列能夠表示的數(狀態)有幾種,,就是線性基的大小的2次方,,,
// luogu-judger-enable-o2 #include <bits/stdc++.h> // #include <iostream> // #include <cstdio> // #include <cstdlib> // #include <string.h> // #include <vector> // #include <algorithm> // #include <set> // #include <vector> // #include <cmath> // #include <queue> // #include <stack> // #include <ctime> // #include <random> #define aaa cout<<233<<endl; #define endl '\n' #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef long double ld; // mt19937 rnd(time(0)); const int inf = 0x3f3f3f3f;//1061109567 > 1e9 const ll linf = 0x3f3f3f3f3f3f3f; const double eps = 1e-5; const double pi = 3.14159265358979; const int maxn = 1e4 + 5; const int maxm = 1e4 + 5; const int mod = 1e9 + 7; struct LinearBasis { static const int maxbase = 62; ll a[maxbase + 1]; LinearBasis() { memset(a, 0, sizeof a); } LinearBasis(ll *x, int n) { LinearBasis(); build(x, n); } void build(ll *x, int n) { for(int i = 1; i <= n; ++i) insert(x[i]); } bool insert(ll t) { for(int i = maxbase; i >= 0; --i) { if(t & (1ll << i)) { if(!a[i]) { a[i] = t; break; } t ^= a[i]; } } return t; } void Insert(ll t) { for(int i = maxbase; i >= 0; --i) { if(t >> i & 1) { if(a[i])t ^= a[i]; else { a[i] = t; for(int j = i - 1; j >= 0; --j)if(a[j] && (a[i] >> j & 1))a[i] ^= a[j]; for(int j = i + 1; j <= maxbase; ++j)if(a[j] >> j & 1)a[j] ^= a[i]; break; } } } } LinearBasis merge(const LinearBasis &l1, const LinearBasis &l2) { LinearBasis ret = l1; for(int i = maxbase; i >= 0; --i) if(l2.a[i]) ret.insert(l2.a[i]); return ret; } ll queryMax() { ll ret = 0; for(int i = maxbase; i >= 0; --i) if((ret ^ a[i]) > ret) ret ^= a[i]; return ret; } ll queryMin() { for(int i = 0; i <= maxbase; ++i) if(a[i]) return a[i]; return 0; } }; char ch[55]; int main() { // double pp = clock(); // freopen("233.in", "r", stdin); // freopen("233.out", "w", stdout); ios_base::sync_with_stdio(0); cin.tie(0);cout.tie(0); int n, m; cin >> m >> n; LinearBasis l; int ans = 0; for(int i = 1; i <= n; ++i) { cin >> ch; ll t = 0; for(int j = 0; j <= m - 1; ++j) { if(ch[j] == 'O')t |= 1; t <<= 1; } t >>= 1; if(l.insert(t))++ans; } cout << (1ll << ans) % 2008 << endl; // cout << endl << (clock() - pp) / CLOCKS_PER_SEC << endl; return 0; }
這題的大意是一個樹,有點權,問你對於樹上任意兩點的路徑 \(u->v\) 的點權的異或和的最大值是多少,,
樹上任意兩點間的路徑就是問 LCA ,,因此用那幾種求LCA的方法就能夠了,,以前看過樹鏈剖分,,可是忘得差很少了,,撿起來重學了下,,
就和LCA的題同樣,不過是線段樹等數據結構維護的之不一樣了,,之前是和、最值什麼的,,這題改爲線性基就能夠了,,維護一條路徑的線性基,,而後進行線性基的合併就能夠了,,,(數據數組開成int炸了好幾發re
// luogu-judger-enable-o2 // luogu-judger-enable-o2 #include <bits/stdc++.h> // #include <iostream> // #include <cstdio> // #include <cstdlib> // #include <string.h> // #include <vector> // #include <algorithm> // #include <set> // #include <vector> // #include <cmath> // #include <queue> // #include <stack> // #include <ctime> // #include <random> #define aaa cout<<233<<endl; #define endl '\n' #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef long double ld; // mt19937 rnd(time(0)); const int inf = 0x3f3f3f3f;//1061109567 > 1e9 const ll linf = 0x3f3f3f3f3f3f3f; const double eps = 1e-5; const double pi = 3.14159265358979; const int maxn = 2e4 + 5; const int maxm = 1e4 + 5; const int mod = 1e9 + 7; struct LinearBasis { static const int maxbase = 63; ll a[maxbase + 1]; LinearBasis() { memset(a, 0, sizeof a); } LinearBasis(ll *x, int n) { LinearBasis(); build(x, n); } void build(ll *x, int n) { for(int i = 1; i <= n; ++i) insert(x[i]); } void clear() { memset(a, 0, sizeof a); } bool insert(ll t) { for(int i = maxbase; i >= 0; --i) { if(t & (1ll << i)) { if(!a[i]) { a[i] = t; break; } t ^= a[i]; } } return t; } void Insert(ll t) { for(int i = maxbase; i >= 0; --i) { if(t >> i & 1) { if(a[i])t ^= a[i]; else { a[i] = t; for(int j = i - 1; j >= 0; --j)if(a[j] && (a[i] >> j & 1))a[i] ^= a[j]; for(int j = i + 1; j <= maxbase; ++j)if(a[j] >> j & 1)a[j] ^= a[i]; break; } } } } LinearBasis merge(const LinearBasis &l1, const LinearBasis &l2) { LinearBasis ret = l1; for(int i = maxbase; i >= 0; --i) if(l2.a[i]) ret.insert(l2.a[i]); return ret; } void merge(const LinearBasis &r) { for(int i = maxbase; i >= 0; --i) if(r.a[i]) insert(r.a[i]); return; } ll queryMax() { ll ret = 0; for(int i = maxbase; i >= 0; --i) if((ret ^ a[i]) > ret) ret ^= a[i]; return ret; } ll queryMin() { for(int i = 0; i <= maxbase; ++i) if(a[i]) return a[i]; return 0; } }; struct edge { int to, nxt; }edge[maxn * 3]; int tot, head[maxn * 3]; int top[maxn * 3]; int fa[maxn * 3]; int dep[maxn * 3]; int num[maxn * 3]; int p[maxn * 3], fp[maxn * 3]; int son[maxn * 3]; int pos; ll a[maxn], w[maxn << 2]; void init() { tot = 0; memset(head, -1, sizeof head); pos = 0; memset(son, -1, sizeof son); memset(w, 0, sizeof w); } void addedge(int u, int v) { edge[tot].to = v; edge[tot].nxt = head[u]; head[u] = tot++; } void dfs1(int u, int pre, int d) { dep[u] = d; fa[u] = pre; num[u] = 1; for(int i = head[u]; ~i; i = edge[i].nxt) { int v = edge[i].to; if(v != pre) { dfs1(v, u, d + 1); num[u] += num[v]; if(son[u] == -1 || num[v] > num[son[u]]) son[u] = v; } } } void dfs2(int u, int sp) { top[u] = sp; p[u] = ++pos; fp[p[u]] = u; w[pos] = a[u]; if(son[u] == -1)return; dfs2(son[u], sp); for(int i = head[u]; ~i; i = edge[i].nxt) { int v = edge[i].to; if(v != son[u] && v != fa[u]) dfs2(v, v); } } struct node { int l, r; LinearBasis lb; }node[maxn * 6]; void pushup(int rt) { node[rt].lb.merge(node[rt << 1].lb); node[rt].lb.merge(node[rt << 1 | 1].lb); // cout << endl;for(int i = 0; i <= 5; i ++)cout << node[rt << 1].lb.a[i] << "-";cout << endl << endl; // cout << endl;for(int i = 0; i <= 5; i ++)cout << node[rt << 1 | 1].lb.a[i] << "+";cout << endl << endl; // cout << endl;for(int i = 0; i <= 5; i ++)cout << node[rt].lb.a[i] << " ";cout << endl << endl; } void build(int rt, int l, int r) { node[rt].l = l; node[rt].r = r; if(l == r){node[rt].lb.insert(w[l]);return;} int mid = l + r >> 1; build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r); pushup(rt); return; } LinearBasis ret; void query(int rt, int l, int r) { if(node[rt].l == l && node[rt].r == r) { ret.merge(node[rt].lb); return; } int mid = node[rt].l + node[rt].r >> 1; if(r <= mid)query(rt << 1, l, r); else if(l > mid)query(rt << 1 | 1, l, r); else query(rt << 1, l, mid), query(rt << 1 | 1, mid + 1, r); } ll getAns(int u, int v) { int f1 = top[u], f2 = top[v]; LinearBasis ans; while(f1 != f2) { if(dep[f1] < dep[f2]) { swap(f1, f2); swap(u, v); } ret.clear(); query(1, p[f1], p[u]); // cout << endl; cout << p[f1] << " " << p[u] << " " << f1 << " " << u << endl;for(int i = 0; i <= 5; ++i)cout << ret.a[i] << " ";cout<< endl; // cout <<ret.queryMax() << "----------------------" << endl; ans.merge(ret); u = fa[f1]; f1 = top[u]; } ret.clear(); if(dep[u] > dep[v])swap(u, v); query(1, p[u], p[v]); ans.merge(ret); return ans.queryMax(); } int main() { // double pp = clock(); // freopen("233.in", "r", stdin); // freopen("233.out", "w", stdout); ios_base::sync_with_stdio(0); cin.tie(0);cout.tie(0); init(); int n, q; cin >> n >> q; for(int i = 1; i <= n; ++i)cin >> a[i]; int u, v; for(int i = 1; i <= n - 1; ++i) { cin >> u >> v; addedge(u, v); addedge(v, u); } dfs1(1, 0, 1); dfs2(1, 1); build(1, 1, pos); while(q--) { cin >> u >> v; cout << getAns(u, v) << endl; } // for(int i = 5; i >= 0; --i) // cout << node[1].lb.a[i] << endl; // ret.clear(); // query(1, p[4], p[4]); // cout << ret.queryMax() << endl; // cout << endl << (clock() - pp) / CLOCKS_PER_SEC << endl; return 0; }
這題求得是一個區間線性基的並,,題目大意是給你一堆數集 \(a_i\) ,, 而後一些詢問 \(l, r, x\) 問你 \(a_l....a_r\) 的每個集合可否異或出 \(x\) ,,,
暴力線性基查詢確定會T (說的就是我,,,,,
詢問的是一個區間的每個集合可否能夠異或出數 \(x\) ,,反過來想,,就是存在不存在一組線性基能夠表示 \(x\) 的狀況下同時是每一組的一個子集,,,也就是說這些集合線性基的交可否表示出 \(x\) ,, 一個線性基可否表示數很簡單,,關鍵就是線性基的求交,,,弄好這個就能夠用線段樹維護區間的線性基的交,,,對於詢問,最直接的想法就是求出詢問區間的交,而後查看是否能夠表示出數 \(x\) ,,可是這樣不必,可能會T ,,,(別問我爲何,,, ,,只要判斷每個詢問的子區間的交是否能夠表示便可,,把這些區間結果合併與就是答案,,,
(有一次把ll寫成int,瘋狂WA,, 還有線性基的交魔改代碼爆炸 ,,,,
#include <bits/stdc++.h> // #include <iostream> // #include <cstdio> // #include <cstdlib> // #include <string.h> // #include <vector> // #include <algorithm> // #include <set> // #include <vector> // #include <cmath> // #include <queue> // #include <stack> // #include <ctime> // #include <random> #define aaa cout<<233<<endl; #define endl '\n' #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef long double ld; // mt19937 rnd(time(0)); const int inf = 0x3f3f3f3f;//1061109567 > 1e9 const ll linf = 0x3f3f3f3f3f3f3f3f; const double eps = 1e-5; const double pi = 3.14159265358979; const int maxn = 5e4 + 5; const int maxm = 1e4 + 5; const int mod = 1e9 + 7; struct LinearBasis { static const int maxbase = 35; bool flag = false; ll a[maxbase + 1]; LinearBasis() { // memset(a, 0, sizeof a); } LinearBasis(ll *x, int n) { LinearBasis(); build(x, n); } void build(ll *x, int n) { for(int i = 1; i <= n; ++i) insert(x[i]); } void clear() { memset(a, 0, sizeof a); } bool insert(ll t) { for(int i = maxbase; i >= 0; --i) { if(t & (1ll << i)) { if(!a[i]) { a[i] = t; break; } t ^= a[i]; } } if(t == 0)flag = true; return t; } bool query(ll t) { if(t > queryMax())return false; if(t == 0)return true; for(int i = maxbase; i >= 0; --i) { if(t & (1ll << i)) { if(!a[i]) { return false; } t ^= a[i]; } } return true; } void Insert(ll t) { for(int i = maxbase; i >= 0; --i) { if(t >> i & 1) { if(a[i])t ^= a[i]; else { a[i] = t; for(int j = i - 1; j >= 0; --j)if(a[j] && (a[i] >> j & 1))a[i] ^= a[j]; for(int j = i + 1; j <= maxbase; ++j)if(a[j] >> j & 1)a[j] ^= a[i]; break; } } } } LinearBasis merge(const LinearBasis &l1, const LinearBasis &l2) { LinearBasis ret = l1; for(int i = maxbase; i >= 0; --i) if(l2.a[i]) ret.insert(l2.a[i]); return ret; } LinearBasis intersection(const LinearBasis &l1, const LinearBasis &l2) { LinearBasis all, ret, full; ret.clear(); for(int i = maxbase; i >= 0; --i) { all.a[i] = l1.a[i]; full.a[i] = 1ll << i; } for(int i = maxbase; i >= 0; --i) { if(l2.a[i]) { ll v = l2.a[i], k = 0; bool flag = true; for(int j = maxbase; j >= 0; --j) { if(v & (1ll << j)) { if(all.a[j]) { v ^= all.a[j]; k ^= full.a[j]; } else { // l2's basis is not in l1's; flag = false; all.a[j] = v; full.a[j] = k; break; } } } if(flag) { ll v = 0; // get intersection by k; for(int j = maxbase; j >= 0; --j) { if(k & (1ll << j)) { v ^= l1.a[j]; } } ret.insert(v); //save ans } } } return ret; } ll queryMax() { ll ret = 0; for(int i = maxbase; i >= 0; --i) if((ret ^ a[i]) > ret) ret ^= a[i]; return ret; } ll queryMin() { for(int i = 0; i <= maxbase; ++i) if(a[i]) return a[i]; return 0; } }lb[maxn]; LinearBasis node[maxn << 2]; void pushup(int rt) { node[rt] = node[rt].intersection(node[rt << 1], node[rt << 1 | 1]); } void build(int rt, int l, int r) { if(l == r) { node[rt] = lb[l]; return; } int mid = l + r >> 1; build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r); pushup(rt); return; } LinearBasis ans; bool query(int rt, int l, int r, int L, int R, ll x) { if(L <= l && r <= R) { return node[rt].query(x); } int mid = l + r >> 1; bool flag1, flag2; flag1 = flag2 = true; if(L <= mid)flag1 = query(rt << 1, l, mid, L, R, x); if(R > mid)flag2 = query(rt << 1 | 1, mid + 1, r, L, R, x); return flag1 & flag2; } int main() { // double pp = clock(); // freopen("233.in", "r", stdin); // freopen("233.out", "w", stdout); ios_base::sync_with_stdio(0); cin.tie(0);cout.tie(0); int n, m; cin >> n >> m; for(int i = 1; i <= n; ++i) { int num; cin >> num; ll x; for(int j = 1; j <= num; ++j) { cin >> x; lb[i].insert(x); } } build(1, 1, n); int l, r;ll x; while(m--) { cin >> l >> r >> x; if(query(1, 1, n, l, r, x))cout << "YES" << endl; else cout << "NO" << endl; } // cout << endl << (clock() - pp) / CLOCKS_PER_SEC << endl; return 0; }
這題的大意是對於給定的數組,有兩個操做,一個是詢問一個區間的異或和的最大值,,另外一個是在這個數組後面增長一個值,,,
這題也是誘使我學線性基的緣由,,
題解說直接數據結構維護會T,,我也沒試,,正解是貪心的維護一個 前綴線性基 ,在每插入一個數時,,若是能插入,,儘量的插到高位,,(這樣能夠保證靠近r的能夠插入的數儘量的在高位,,
也就是說,,對於任意的任意的一個區間,,無論它的長度多大,,,他的線性基最可能是30個(針對這題),,,因此咱們只須要維護r前面出現的較晚的新基,,這樣每次詢問,,都看得在r處的線性基中出現比l晚的基便可,,爲了實現這個過程,,,給每個線性基中的每一位都加一個標誌位 \(p_i\) ,, 在插入一個新的數時,,,儘量的把他放在高位,,,(碰到一個能夠插入的位置時,把他插在這裏,,而後下推其它的基,,,
這題不能莽,直接開ll,,,會mle,,,
#include <bits/stdc++.h> // #include <iostream> // #include <cstdio> // #include <cstdlib> // #include <string.h> // #include <vector> // #include <algorithm> // #include <set> // #include <vector> // #include <cmath> // #include <queue> // #include <stack> // #include <ctime> // #include <random> #define aaa cout<<233<<endl; #define endl '\n' #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef long double ld; // mt19937 rnd(time(0)); const int inf = 0x3f3f3f3f;//1061109567 > 1e9 const ll linf = 0x3f3f3f3f3f3f3f3f; const double eps = 1e-5; const double pi = 3.14159265358979; const int maxn = 5e5 + 5; const int maxm = 1e4 + 5; const int mod = 1e9 + 7; struct LinearBasis { typedef int type; static const int maxbase = 30; bool flag = false; type a[maxbase + 1]; type p[maxbase + 1]; LinearBasis() { memset(a, 0, sizeof a); memset(p, 0, sizeof p); } LinearBasis(type *x, int n) { LinearBasis(); build(x, n); } void build(type *x, int n) { for(int i = 1; i <= n; ++i) insert(x[i]); } void clear() { memset(a, 0, sizeof a); memset(p, 0, sizeof p); } bool insert(type t) { //暴力插入一個數,維護的是一個上三角型的線性基矩陣,時間複雜度低,當待插入元素能插入時,返回true for(int i = maxbase; i >= 0; --i) { if(t & (1ll << i)) { if(!a[i]) { a[i] = t; break; } t ^= a[i]; } } if(t == 0)flag = true; return t; } bool insert2(type t, type pos) { for(int i = maxbase; i >= 0; --i) { if(t & (1ll << i)) { if(!a[i]) { a[i] = t; p[i] = pos; break; } else if(pos > p[i]) { swap(pos, p[i]); swap(t, a[i]); } t ^= a[i]; } } if(t == 0)flag = true; return t; } bool query(type t) { // 詢問t是否能夠被當前線性基表示,不插入 if(t > queryMax())return false; if(t == 0)return true; for(int i = maxbase; i >= 0; --i) { if(t & (1ll << i)) { if(!a[i]) { return false; } t ^= a[i]; } } return true; } void Insert(type t) { //插入一個線性基,利用高斯消元法維護一個對角矩陣 for(int i = maxbase; i >= 0; --i) { if(t >> i & 1) { if(a[i])t ^= a[i]; else { a[i] = t; for(int j = i - 1; j >= 0; --j)if(a[j] && (a[i] >> j & 1))a[i] ^= a[j]; for(int j = i + 1; j <= maxbase; ++j)if(a[j] >> j & 1)a[j] ^= a[i]; break; } } } } LinearBasis merge(const LinearBasis &l1, const LinearBasis &l2) { // 獲得兩個線性基的並 LinearBasis ret = l1; for(int i = maxbase; i >= 0; --i) if(l2.a[i]) ret.insert(l2.a[i]); return ret; } LinearBasis intersection(const LinearBasis &l1, const LinearBasis &l2) { //獲得兩個線性基的交 LinearBasis all, ret, full; ret.clear(); for(int i = maxbase; i >= 0; --i) { all.a[i] = l1.a[i]; full.a[i] = 1ll << i; } for(int i = maxbase; i >= 0; --i) { if(l2.a[i]) { type v = l2.a[i], k = 0; bool flag = true; for(int j = maxbase; j >= 0; --j) { if(v & (1ll << j)) { if(all.a[j]) { v ^= all.a[j]; k ^= full.a[j]; } else { // l2's basis is not in l1's; flag = false; all.a[j] = v; full.a[j] = k; break; } } } if(flag) { type v = 0; // get intersection by k; for(int j = maxbase; j >= 0; --j) { if(k & (1ll << j)) { v ^= l1.a[j]; } } ret.insert(v); //save ans } } } return ret; } //詢問最值 type queryMax() { type ret = 0; for(int i = maxbase; i >= 0; --i) if((ret ^ a[i]) > ret) ret ^= a[i]; return ret; } type queryMax(type l) { type ret = 0; for(int i = maxbase; i >= 0; --i) if((ret ^ a[i]) > ret && l <= p[i]) ret ^= a[i]; return ret; } type queryMin() { for(int i = 0; i <= maxbase; ++i) if(a[i]) return a[i]; return 0; } }lb[maxn]; int main() { // double pp = clock(); // freopen("233.in", "r", stdin); // freopen("233.out", "w", stdout); ios_base::sync_with_stdio(0); cin.tie(0);cout.tie(0); int t; cin >> t; while(t--) { int n, m; cin >> n >> m; ll x; for(int i = 1; i <= n; ++i) { cin >> x; lb[i] = lb[i - 1]; lb[i].insert2(x, i); } int op; ll lstans = 0; while(m--) { cin >> op; if(!op) { ll l, r;cin >> l >> r; l = (l ^ lstans) % n + 1; r = (r ^ lstans) % n + 1; if(l > r)swap(l, r); lstans = lb[r].queryMax(l); cout << lstans << endl; } else { ll x;cin >> x; x ^= lstans; lb[++n] = lb[n - 1]; lb[n].insert2(x, n); } } } // cout << endl << (clock() - pp) / CLOCKS_PER_SEC << endl; return 0; }
還有幾道題有時間再補把,,,,好比西安區域賽那道 ,,cf這道,,貌似是杭電的原型題,,,