线性基学习笔记

我是看了有一道用线性基判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 中的任意一个异或和都可以表示S中的异或和,同时满足 V V 的大小尽可能小的条件。

线性基的应用:

  1. 可以查找一个数是否在S的异或集合中。

    • 查找 S S 的异或集合中的最大值。
    • 查找S的异或集合中的第k大。
    • 线性基的构造:

      线性基至多有 logamax log ⁡ a m a x 位。如果 bi0 b i ≠ 0 bi b i 的最高位为第i位。
      每次输入一个数 x x ,我们从高到低扫,如果最高位上有1,那么判断是否有这一位的线性基,若果没有则令bi=x,否则 x=xbi x = x ⊕ b i ,这就相当于把 x x <script type="math/tex" id="MathJax-Element-956">x</script>减去这一位的线性基。
      一个数如果可以被原来的线性基表示,则在过程中会变成0,否则会被新添加进去线性基。

      查询:

      若要查询是否可以被异或出来,可以仿照构造的方法,只要中间变成了0,就说明可以被表示出来。
      若要查询最大值,从大到小扫每一位,若异或上这个数可以使答案变得更大,则异或上这个数。
      查询第k大:

      考虑k也是一个二进制数,而线性基中的数是按位来的(最大的那一位),那么我们就可以用有效的线性基来表示这个二进制数k。把有效的线性基单独取出来放到一个vector里面,然后把每一个线性基单独表示每一位就好了。
      但是这样会错。因为大的线性基中可能包含了小的线性基中的最高位。所以我们要重新构造线性基,即把大的线性基中包含了小线性基中的最高位的那一位给消掉就可以了。

      模板:

      luogu3812
      hdu3949

      /*============================
       * 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;
      }
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值