【题解】魔改版线性基

【题解】魔改版线性基

魔改版线性基解决此类问题。

联系线性空间的性质,我们直接可以构造出这样的基:
\[
100000
\010000
\000010
\000001
\]
使得每个基的最高位是唯一的,我们的目的是要能够保证从上往下一直异或一直变大,所以不能使基出现这样的情况:
\[
100001
\000001
\]

一个不能从上往下一直异或一直变大的例子。

考虑如何构造\(kth\) 大(小),考虑这样的性质,我们记\(a_i\)表示从下往上第\(i\)个基,显然从\(0\)开始,如果我们异或了\(a_x\),那么我们可以保证我们会比这个线性空间内\(2^{x-1}\)个值都要大,直接考虑从上往下查询,类似树上倍增。

考虑无解的情况,显然当我们发现\(k \ge 2^{|E|}\)时就无解了。

然而我们有一个问题,那就是我们默认\(0\)是可以被表示出来的,然而我们知道,当给定元素全部互为不相关时,这个时候就不存在一个\(0\)会被表示出来了。那么我们可以特判一下即可 。

好,那么如何构造从上往下一直异或一直变大的基呢?魔改一下就好了。具体看代码。

分析复杂度:我们把魔改操作的\(log^2\)看做常数吧...那就是\(O(nlogn)\)

当然你如果不承认魔改操作是常数,那就继续魔改\(upd\)函数,也可以做到\(nlogn\)。具体就是加入的时候从小往大加。

下面的第\(k\)小,转换为第\(k\)大用\(2^{|E|}?\)减去即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template <class ccf>
inline ccf qr(ccf ret) {
      ret = 0;
      register char c = getchar();
      while (c < 48 || c > 57) c = getchar();
      while (c >= 48 && c <= 57) ret = ret * 10 + c - 48, c = getchar();
      return ret;
}
namespace bs {
      ll num[51], sav[51];
      struct BASE {
        ll data[51];
        int cnt;
        ll p;
        BASE() {
          memset(data, 0, sizeof data);
          cnt = 0;
          p = 1;
        }
        inline ll size() { return 1LL << cnt; }
        inline void set() {
          for (register int t = 1; t <= 50; ++t)
            if (data[t])
                  for (register int i = t + 1; i <= 50; ++i)
                    if (data[i] & num[t])
                      data[i] ^= data[t];
        }
        inline void upd(ll x) {
          for (register int t = 50; t >= 1; --t) {
            if (x & num[t]) {
                  if (not data[t]) {
                    data[t] = x;
                    ++cnt;
                    break;
                  } else
                    x ^= data[t];
            }
            if(not x) {
                  p=0;
                  break;
            }
          }
        }
        inline ll que(ll k) {
          register ll ret = 0, sav = 0;
          if(cnt==50) --k;
          if (k > this->size())
            return -1;
          for (register int t = 50, f = 0; t >= 1; --t) {
            if (data[t]) {
                  ++f;
                  if (sav + (1LL << (cnt - f)) <= k)
                    sav += 1LL << (cnt - f), ret = ret ^ data[t];
            }
          }
          return ret;
        }
        inline void clear() {
          memset(data, 0, sizeof data);
          cnt = 0;
        }
        inline void debug() {
          for (register int t = 50; t >= 1; --t) {
            if (data[t])
                  cout << ((bitset<10>)data[t]) << ' ' << data[t] << endl;
          }
          cout << endl;
        }
      };
      inline void init() {
        for (num[0] = 2, num[1] = 1; num[0] <= 50; ++num[0]) num[num[0]] = num[num[0] - 1] << 1;
      }
}
bs::BASE qaq;

int main() {
      bs::init();
      int n = qr(1);
      for (register int t = 1; t <= n; ++t) qaq.upd(qr(1ll));
      int m = qr(1);
      qaq.set();
      for (register int t = 1; t <= m; ++t) printf("%lld\n", qaq.que(qr(1ll)));
      return 0;
}

原文地址:https://www.cnblogs.com/winlere/p/10602810.html

时间: 2024-08-30 14:30:27

【题解】魔改版线性基的相关文章

题解 外星千足虫(线性基+高斯消元)

题解 luogu外星千足虫(线性基+高斯消元) 题目 luogu题目传送门 题解想法 首先需要知道这是个异或方程对吧 然后既然看到位运算,又有这么多,就可以考虑线性基(做题技巧),那我们就丢进去 接下来看一看线性基,哇,性质美妙 它不就是Gauss消元里面想要的上三角矩阵吗 所以说: 如果能拼成线性基,那么枚举到哪里完成了,就输出位置(first_ans) 如果拼不成,那就解不出(毋庸置疑) 那真是美妙啊... 所以怎么消元呢?这可是个异或方程,我们要解出来啊 枚举整个线性基的g[i] 如果g[

【题解】 bzoj2460: [BeiJing2011]元素 (线性基)

bzoj2460,戳我戳我 Solution: 线性基板子,没啥好说的,注意long long 就好了 Code: //It is coded by Ning_Mew on 5.29 #include<bits/stdc++.h> #define LL long long using namespace std; const int maxn=1007; int n; LL A[maxn]; struct Node{ LL num;int val; }s[maxn]; LL ans=0; bo

【题解】 luogu 3857 [TJOI2008]彩灯 (线性基)

luogu3857,懒得复制 Solution: 裸的线性基,往里面添加数,记录添加个数\(sum\),快速幂输出\(2^{sum}\)即可 Code: //It is coded by Ning_Mew on 5.30 #include<bits/stdc++.h> #define LL long long using namespace std; const int maxn=70,MOD=2008; int n,m; LL s[maxn],x[maxn],ans=0; LL read()

【题解】 bzoj1923: [Sdoi2010]外星千足虫 (线性基/高斯消元)

bzoj1923,戳我戳我 Solution: 这个高斯消元/线性基很好看出来,主要是判断在第K 次统计结束后就可以确定唯一解的地方和\(bitset\)的骚操作 (我用的线性基)判断位置,我们可以每次加入一个线性基时判断是不是全被异或掉了,如果没有,说明这个方程不是冗余的,那么我们可记录非冗余方程个数 如果非冗余方程个数小于\(n\),那就是个不定方程组,有无数种解,否则,在个数第一次达到\(n\)时,就可输出当时输入方程的号码 还有一个点就是压空间与时间,这题主要是时间,用到大杀器\(bit

【bzoj2460】【beijing2011】【元素】【线性基+贪心】

Description 相传,在远古时期,位于西方大陆的 Magic Land 上,人们已经掌握了用魔 法矿石炼制法杖的技术.那时人们就认识到,一个法杖的法力取决于使用的矿石. 一般地,矿石越多则法力越强,但物极必反:有时,人们为了获取更强的法力而 使用了很多矿石,却在炼制过程中发现魔法矿石全部消失了,从而无法炼制 出法杖,这个现象被称为"魔法抵消" .特别地,如果在炼制过程中使用超过 一块同一种矿石,那么一定会发生"魔法抵消". 后来,随着人们认知水平的提高,这个

[BeiJing2011]元素[贪心+线性基]

2460: [BeiJing2011]元素 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1245  Solved: 652[Submit][Status][Discuss] Description 相传,在远古时期,位于西方大陆的 Magic Land 上,人们已经掌握了用魔法矿石炼制法杖的技术.那时人们就认识到,一个法杖的法力取决于使用的矿石.一般地,矿石越多则法力越强,但物极必反:有时,人们为了获取更强的法力而使用了很多矿石,却在炼制过程中

【BZOJ2844】albus就是要第一个出场 线性基 高斯消元

#include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/43456773"); } 题意:需要注意的是空集(0)是天生被包括的,我为了这个WA了好久~拍了好久,醉了好久~ 题解: 首先有一个我并不知道是为什么(甚至不知道它对不对)的性质: 每一种权值会出现2的自由元(n-线性基个数)次方 次. 感性

BZOJ 2844 albus就是要第一个出场 ——高斯消元 线性基

[题目分析] 高斯消元求线性基. 题目本身不难,但是两种维护线性基的方法引起了我的思考. 1 2 3 4 5 6 7 8 9 10 11 12 void gauss(){     k=n;     F(i,1,n){         F(j,i+1,n) if (a[j]>a[i]) swap(a[i],a[j]);         if (!a[i]) {k=i-1; break;}         D(j,30,0) if (a[i]>>j & 1){            

【bzoj4184】shallot 线段树+高斯消元动态维护线性基

题目描述 小苗去市场上买了一捆小葱苗,她突然一时兴起,于是她在每颗小葱苗上写上一个数字,然后把小葱叫过来玩游戏. 每个时刻她会给小葱一颗小葱苗或者是从小葱手里拿走一颗小葱苗,并且 让小葱从自己手中的小葱苗里选出一些小葱苗使得选出的小葱苗上的数字的异或和最大. 这种小问题对于小葱来说当然不在话下,但是他的身边没有电脑,于是他打电话给同为Oi选手的你,你能帮帮他吗? 你只需要输出最大的异或和即可,若小葱手中没有小葱苗则输出0. 输入 第一行一个正整数n表示总时间:第二行n个整数a1,a2...an,