【題解】[SCOI 2019] 函式【線性基】
阿新 • • 發佈:2020-12-25
題意
給定 \(a_{1\cdots n},w\)。有 \(n\) 個 01 向量,第 \(i\) 個為 \(a_i,a_i+1,a_i+2,\cdots a_i+w\) 轉化為一定長度 \(L\) 的二進位制後拼接起來。求這些向量組成矩陣的軼(異或意義下)。\(n,w,a_i\leq 2^{17}\)
題解
嘗試通過做列變換簡化矩陣。
以每 \(L\) 列為一組,先把後一組異或上前一組,得到每行形如 xxxxxx|000111|000001|000011|000001|001111...
。
將組內每一列異或上前一列,得到每行形如 xxxxxx|000100|000001|000010|000001|001000...
。
找到 1 的位置最靠前的一組,這個 1(下面將其稱為“關鍵 1”)的位置確定後,其他 1 的位置也都能確定。故以組內位置為第一關鍵字,組間順序為第二關鍵字,挨個假設每個位置是關鍵 1,用這一列去異或在此假設下其他為 1 的列。在遇到真正的關鍵 1 之前,我們都在用 0 異或其他數,故沒有影響;關鍵 1 會把其他所有位置變成 0,所以每行後面部分只剩一個關鍵 1。
開始線性基:先以關鍵 1 為主元,在後面消元;若有關鍵 1 相同的行,選定一行放入線性基,其他行異或它後用前 17 位進行標準的線性基。
#include<bits/stdc++.h> using namespace std; const int N=2e5+10,L=19,mod=998244353; int n,w,a[N],b[L]; map<pair<int,int>,int>mp; void ins(int x){ for(int i=L-1;i>=0;--i){ if((x>>i)&1){ swap(b[i],x); if((x>>i)&1)x^=b[i]; } } } int main(){ int n,w; scanf("%d%d",&n,&w); for(int i=0;i<n;i++)scanf("%d",a+i); for(int i=0;i<n;i++){ int k=a[i]^(a[i]+w),q=0; while(k>>q)++q; pair<int,int>p(q,(((a[i]>>(q-1))+1)<<(q-1))-a[i]); if(!w)ins(a[i]); else if(mp.count(p))ins(mp[p]^a[i]); else mp[p]=a[i]; } int r=mp.size(); for(int i=0;i<L;i++)if(b[i])++r; int t=1;while(r--)t<<=1,t>=mod&&(t-=mod); cout<<t; }