我是看了有一道用线性基判SG函数的题目才知道有这样一种算法的。。。
线性基:
并不会什么向量的解释,我的理解的话,就是对于一个数集 S={a1,a2,a3...} S = { a 1 , a 2 , a 3 . . . } ,重新构造一个集合 V={b1,b2,b3...} V = { b 1 , b 2 , b 3 . . . } ,使得 V V 中的任意一个异或和都可以表示中的异或和,同时满足 V V 的大小尽可能小的条件。
线性基的应用:
- 可以查找一个数是否在的异或集合中。
- 查找 S S 的异或集合中的最大值。
- 查找的异或集合中的第k大。
线性基的构造:
线性基至多有 logamax log a m a x 位。如果 bi≠0 b i ≠ 0 则 bi b i 的最高位为第i位。
每次输入一个数 x x ,我们从高到低扫,如果最高位上有1,那么判断是否有这一位的线性基,若果没有则令,否则 x=x⊕bi x = x ⊕ b i ,这就相当于把 x x <script type="math/tex" id="MathJax-Element-956">x</script>减去这一位的线性基。
一个数如果可以被原来的线性基表示,则在过程中会变成0,否则会被新添加进去线性基。查询:
若要查询是否可以被异或出来,可以仿照构造的方法,只要中间变成了0,就说明可以被表示出来。
若要查询最大值,从大到小扫每一位,若异或上这个数可以使答案变得更大,则异或上这个数。
查询第k大:
考虑k也是一个二进制数,而线性基中的数是按位来的(最大的那一位),那么我们就可以用有效的线性基来表示这个二进制数k。把有效的线性基单独取出来放到一个vector里面,然后把每一个线性基单独表示每一位就好了。
但是这样会错。因为大的线性基中可能包含了小的线性基中的最高位。所以我们要重新构造线性基,即把大的线性基中包含了小线性基中的最高位的那一位给消掉就可以了。模板:
/*============================ * Author : ylsoi * Prolem : luogu3812 * Algorithm : Linear Basis * Time : 2018.6.4 * ==========================*/ #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cmath> #include<climits> using namespace std; void File(){ freopen("luogu3812.in","r",stdin); freopen("luogu3812.out","w",stdout); } template<typename T>bool chkmax(T &_,T __){return _<__ ? (_=__,1) : 0;} template<typename T>bool chkmin(T &_,T __){return _>__ ? (_=__,1) : 0;} #define REP(i,a,b) for(register int i=a;i<=b;++i) #define DREP(i,a,b) for(register int i=a;i>=b;--i) #define MREP(i,x) for(register int i=beg[x];i;i=E[i].last) #define mem(a) memset(a,0,sizeof(a)) #define ll long long #define inf INT_MAX const int maxn=50+10; int n; ll b[maxn],ans; void insert(ll x){ DREP(i,50,1){ ll p=1ll<<(i-1); if(!(p&x))continue; if(!b[i]){ b[i]=x; break; } x^=b[i]; } } void rebuild(){ DREP(i,50,1){ if(!b[i])continue; DREP(j,i-1,1){ ll p=1ll<<(j-1); if((b[i]&p) && b[j]) b[i]^=b[j]; } } } void find_max(){ DREP(i,50,1){ ll p=1ll<<(i-1); if(ans&p)continue; ans^=b[i]; } } void out(ll x,int len){ if(!len)return; out(x/2,len-1); printf("%c",x%2==0 ? ' ' : '1'); } int main(){ File(); scanf("%d",&n); REP(i,1,n){ ll tmp; scanf("%lld",&tmp); insert(tmp); } rebuild(); find_max(); printf("%lld\n",ans); return 0; }
/*========================== * Author : ylsoi * Problem : hdu3949 * Algorithm : Linear Basis * Time : 2018.6.5 * ========================*/ #include<iostream> #include<algorithm> #include<cstring> #include<cmath> #include<cstdio> #include<climits> #include<vector> using namespace std; void File(){ freopen("hdu3949.in","r",stdin); freopen("hdu3949.out","w",stdout); } template<typename T>bool chkmax(T &_,T __){return _<__ ? (_=__,1) : 0;} template<typename T>bool chkmin(T &_,T __){return _>__ ? (_=__,1) : 0;} #define REP(i,a,b) for(register int i=a;i<=b;++i) #define DREP(i,a,b) for(register int i=a;i>=b;--i) #define MREP(i,x) for(register int i=beg[x];i;i=E[i].last) #define mem(a) memset(a,0,sizeof(a)) #define ll long long #define inf INT_MAX int T,n,q; ll b[70],siz; bool zero; vector<ll>c; void insert(ll x){ DREP(i,62,1){ if(!x)zero=1; ll p=1ll<<(i-1); if(!(x&p))continue; if(!b[i]){ b[i]=x; break; } x^=b[i]; if(!x)zero=1; } } void rebuild(){ DREP(i,62,1){ if(!b[i])continue; DREP(j,i-1,1){ ll p=1ll<<(j-1); if((b[i]&p) && b[j]) b[i]^=b[j]; } } } ll query(ll k){ if(k==1 && zero)return 0; else if(zero)--k; ll ret=0ll; int size=c.size()-1; REP(i,0,size){ ll p=1ll<<i; if(p&k)ret^=c[i]; } return ret; } int main(){ File(); scanf("%d",&T); REP(cas,1,T){ mem(b); zero=0; siz=1ll; c.clear(); printf("Case #%d:\n",cas); scanf("%d",&n); REP(i,1,n){ ll tmp; scanf("%lld",&tmp); insert(tmp); } rebuild(); REP(i,1,62)if(b[i]){ siz<<=1; c.push_back(b[i]); } if(!zero)--siz; scanf("%d",&q); REP(i,1,q){ ll k; scanf("%lld",&k); if(k>siz)puts("-1"); else printf("%lld\n",query(k)); } } return 0; }