异或线性基
线性基
线性空间下的一组基
对于线性空间
异或线性基的构造
考虑贪心
对于插入数
当
否则
inline bool insert(int x){ for(int i=T;i>=0;i--) if((x>>i)&1) if(b[i]) x^=b[i]; else{ b[i]=x; return true; } return 0; }
最大异或和
考虑贪心
从
inline int gmax(int v){ int ans=v; for(int i=T;i>=0;i--) if((ans^b[i])>ans) ans^=b[i]; return ans; }
最小异或和
否则最小异或和就等于线性基中的最小元素
代码就不给了qwq
求并集
其实不难,把线性基中的元素插入到另外一个线性基中就行了
inline friend F operator +(F x,F y){ F ret=y; for(int i=T;i>=0;i--){ if(x[i]){ ret.insert(x[i]); } } return ret; }
求交集
用的很少,就是把线性基中的元素插入到另外一个线性基中会失败的元素
封装
挺清新的(((
template<int T> struct F{ int b[T+5]; F(){ for(int i=0;i<=T;i++) b[i]=0; } inline bool insert(int x){ for(int i=T;i>=0;i--) if((x>>i)&1) if(b[i]) x^=b[i]; else{ b[i]=x; return true; } return 0; } inline int gmax(int v){ int ans=v; for(int i=T;i>=0;i--) if((ans^b[i])>ans) ans^=b[i]; return ans; } inline int operator [](int x){ return b[x]; } inline friend F operator +(F x,F y){ F ret=y; for(int i=T;i>=0;i--){ if(x[i]){ ret.insert(x[i]); } } return ret; } inline void clear(){ for(int i=T;i>=0;i--) b[i]=0; } };
例题
P3812 【模板】线性基
模板题
复杂度
P4151 [WC2011] 最大XOR和路径
题意:
求从
Solution
路径可以分成环和链,我们可以初始答案为
如果选这个环,则
我们把环全放到线性基里
最后求最大异或和即可。
复杂度
#include<bits/stdc++.h> using namespace std; const int MAXN=65,MAXM=200020,B=63; long long n,m,lb[MAXN+10],ans[MAXM]; inline bool insert(long long x){ for(int i=B;i>=0;i--){ if((x>>i)&1){ if(!lb[i]){ lb[i]=x; return true; } x^=lb[i]; } } return false; } inline long long sum(long long x){ long long ans=x; for(int i=B;i>=0;i--) if((ans^lb[i])>ans) ans^=lb[i]; return ans; } struct node{ int r; long long w; }; vector<node> p[MAXM]; bitset<MAXM+10> vis,cle; inline void dfs(int k,long long sum){ ans[k]=sum,vis[k]=1; for(int i=0;i<p[k].size();i++) if(!vis[p[k][i].r]) dfs(p[k][i].r,sum^p[k][i].w); else insert(sum^p[k][i].w^ans[p[k][i].r]); } int main(){ scanf("%lld%lld",&n,&m); long long x,y,z; for(int i=1;i<=m;i++) scanf("%lld%lld%lld",&x,&y,&z),p[x].push_back({y,z}),p[y].push_back({x,z}); dfs(1,0); printf("%lld\n",sum(ans[n])); return 0; }
CF895C Square Subsets
题意:对于一些数组a,求从中间选择非空子集,使它们的乘积等于某个整数的平方的方法的数量
Solution:
由于
质因数分解
命
将
不在线性基的
复杂度
#include<bits/stdc++.h> using namespace std; int n,cnt,size,x[100010],p[75]; long long mod=1000000007; bool vis[100]; bitset<30> a[30]; inline void insert(int u){ bitset<30> vx; for(int i=1;i<=cnt&&u!=1;i++){ int cntk=0; while(u%p[i]==0) u/=p[i],cntk++; vx[i]=cntk%2; } for(int i=cnt;i>=1;i--){ if(vx[i]){ if(!a[i].any()){ a[i]=vx; size++; return; } vx^=a[i]; } } } long long f(long long a,long long b){ if(a==0) return 1; if(a==1) return b; long long w=f(a/2,b); if(a%2) return w*w%mod*b%mod; return w*w%mod; } int main(){ for(int i=2;i<=75;i++){ if(!vis[i]) p[++cnt]=i; for(int j=1;j<=cnt;j++){ if(p[j]*i>75) break; vis[p[j]*i]=1; } } cin>>n; for(int i=1;i<=n;i++) cin>>x[i]; for(int i=1;i<=n;i++){ insert(x[i]); } printf("%lld\n",(f(n-size,2)-1ll+mod)%mod); return 0; }
P7451 [THUSCH2017] 杜老师
题目大意
给定
思路
因为乘积是个完全平方数,所以自然可以想到乘积的每个质因子的次数为偶数,我们可以把每个质因子的次数看为一维的线性基,最后所有数异或的值为
如果一个数有一个大于
发现只要
code:
#include<bits/stdc++.h> using namespace std; int T; const long long MAXN=10000000,MAXM=1000000,B=455,MXB=7000,mod=998244353; long long l,r,cnt1,cnt2,size,p1[MAXM+10],p2[MAXM+10]; bitset<MAXN+10> vis,vis2,cle2; bitset<470> a[470],cle; map<int,bitset<470>> mp; inline void insert(long long x){ bitset<470> vx; for(int i=1;i<=cnt1&&x!=1;i++){ int cnt=0; while(x%p1[i]==0) cnt++,x/=p1[i]; vx[i]=cnt%2; } if(x!=1){ if(!mp[x].any()&&!vis2[x]){ mp[x]=vx,size++,vis2[x]=1; return; } vx^=mp[x]; } for(int i=B;i>=1;i--){ if(vx[i]){ if(!a[i].any()){ a[i]=vx; size++; return; } vx^=a[i]; } } } long long f(long long a,long long b){ if(b==0) return 1; if(b==1) return a; long long w=f(a,b/2); if(b%2) return w%mod*w%mod*a%mod; return w*w%mod; } inline void init(){ for(long long i=2;i<=MAXN;i++){ if(!vis[i]){ if(i<=3200) p1[++cnt1]=i; p2[++cnt2]=i; if(i*i<=MAXN) for(long long j=i*i;j<=MAXN;j+=i) vis[j]=1; } } } int main(){ scanf("%d",&T); init(); while(T--){ scanf("%lld%lld",&l,&r); map<int,bitset<470>>().swap(mp),size=0,vis2&=cle2; for(int i=0;i<=465;i++) a[i]&=cle; if(r-l+1<=MXB) for(long long i=l;i<=r;i++) insert(i); else for(long long i=1;i<=cnt2&&p2[i]<=r;i++) if(r/p2[i]>(l-1)/p2[i]) size++; printf("%lld\n",f(2,r-l+1-size)); } return 0; }
P4839 P 哥的桶
单点insert,区间最大异或和
考虑线段树,每个节点维护区间线性基,求答案时求并集的最大异或和,复杂度
#include<bits/stdc++.h> #define ll long long #define SF scanf #define PF printf #define PB push_back #define cmax(x,y) x=max(x,y); #define cmin(x,y) x=min(x,y); #define ull unsigned long long #define R register #define rep(i,x,y) for(int i=x;i<=y;i++) #define IOS ios::sync_with_stdio(false) using namespace std; template<int T> struct F{ int b[T+5]; F(){ for(int i=0;i<=T;i++) b[i]=0; } inline bool insert(int x){ for(int i=T;i>=0;i--) if((x>>i)&1) if(b[i]) x^=b[i]; else{ b[i]=x; return true; } return 0; } inline int gmax(int v){ int ans=v; for(int i=T;i>=0;i--) if((ans^b[i])>ans) ans^=b[i]; return ans; } int operator [](int x){ return b[x]; } friend F operator +(F x,F y){ F ret=y; for(int i=T;i>=0;i--){ if(x[i]){ ret.insert(x[i]); } } return ret; } inline void clear(){ for(int i=T;i>=0;i--) b[i]=0; } }; template<int T>struct segtree{ struct node{ int l,r; F<31> v; int add; node(){ l=r=add=0;} }t[T*4+5]; #define l(i) (i<<1) #define r(i) ((i<<1)|1) int a[T+5]; segtree(){ for(int i=1;i<=T;i++) a[i]=0; } void build(int l,int r,int id){ t[id].l=l,t[id].r=r; if(l==r){ return; } int mid=(l+r)>>1; build(l,mid,l(id)); build(mid+1,r,r(id)); } void add(int x,int y,int id){ if(t[id].l==t[id].r){ t[id].v.insert(y); return; } int mid=(t[id].l+t[id].r)>>1; if(x<=mid) add(x,y,l(id)); else add(x,y,r(id)); t[id].v=t[l(id)].v+t[r(id)].v; } F<31> ask(int l,int r,int id){ F<31> ret; if(t[id].l>r||t[id].r<l) return ret; if(t[id].l>=l&&t[id].r<=r) return t[id].v; return ask(l,r,l(id))+ask(l,r,r(id)); } }; segtree<50010> t; int n,m; int main(){ IOS; cin>>n>>m; t.build(1,m,1); while(n--){ int opt,x,y; cin>>opt>>x>>y; if(opt==1) t.add(x,y,1); else printf("%d\n",t.ask(x,y,1).gmax(0)); } return 0; }
P5607 [Ynoi2013] 无力回天 NOI2017
题意:
区间异或,区间最大异或和
区间异或维护原序列很难,发现修改其实只对少部分线段树节点的线性基有影响
类似于差分,用线段树维护数组
维护
最后的线性基与
特判
对于区间修改
线段树修改叶子结点直接重构就行
维护
复杂度
#include<bits/stdc++.h> #define ll long long #define SF scanf #define PF printf #define PB push_back #define cmax(x,y) x=max(x,y); #define cmin(x,y) x=min(x,y); #define ull unsigned long long #define R register #define rep(i,x,y) for(int i=x;i<=y;i++) #define IOS ios::sync_with_stdio(false) using namespace std; template<int T> struct F{ int b[T+5]; F(){ for(int i=0;i<=T;i++) b[i]=0; } inline bool insert(int x){ for(int i=T;i>=0;i--) if((x>>i)&1) if(b[i]) x^=b[i]; else{ b[i]=x; return true; } return 0; } inline int gmax(int v){ int ans=v; // for(int i=T;i>=0;i--) if(b[i]) cout<<i<<' '<<b[i]<<endl; for(int i=T;i>=0;i--) if((ans^b[i])>ans) ans^=b[i]; return ans; } inline int operator [](int x){ return b[x]; } inline friend F operator +(F x,F y){ F ret=y; for(int i=T;i>=0;i--){ if(x[i]){ ret.insert(x[i]); } } return ret; } inline void clear(){ for(int i=T;i>=0;i--) b[i]=0; } }; template<int T>struct BIT{ int c[T+5]; inline int lowbit(int x){ return x&(-x); } inline void xg(int x,int y){ for(int i=x;i<=T;i+=lowbit(i)) c[i]^=y; } inline void add(int l,int r,int w){ xg(l,w); xg(r+1,w); } inline int get(int x){ int ans=0; for(;x>=1;x-=lowbit(x)) ans^=c[x]; return ans; } }; BIT<50050> f; template<int T>struct segtree{ struct node{ int l,r; F<31> v; node(){ l=r=0;} }t[T*4+5]; #define l(i) (i<<1) #define r(i) ((i<<1)|1) int a[T+5]; segtree(){ for(int i=1;i<=T;i++) a[i]=0; } void build(int l,int r,int id){ t[id].l=l,t[id].r=r; for(int i=l;i<=r;i++) t[id].v.insert(a[i]); if(l==r) return; int mid=(l+r)>>1; build(l,mid,l(id)); build(mid+1,r,r(id)); } F<31> ask(int l,int r,int id){ F<31> ret; if(t[id].l>r||t[id].r<l) return ret; if(t[id].l>=l&&t[id].r<=r) return t[id].v; return ask(l,r,l(id))+ask(l,r,r(id)); } void add(int x,int w,int id){ if(t[id].l==t[id].r){ t[id].v.clear(); t[id].v.insert(a[x]^=w); return; } int mid=(t[id].l+t[id].r)>>1; if(x>mid) add(x,w,r(id)); else add(x,w,l(id)); t[id].v=t[l(id)].v+t[r(id)].v; } }; segtree<50010> t; int n,m; inline int read(){ int x=0; char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch-'0'),ch=getchar(); return x; } int main(){ cin>>n>>m; for(int i=1;i<=n;i++) t.a[i]=read(); for(int i=n;i>=2;i--) t.a[i]^=t.a[i-1]; for(int i=1;i<=n;i++) f.xg(i,t.a[i]); t.build(1,n,1); while(m--){ register int opt,x,y,v; opt=read(),x=read(),y=read(),v=read(); if(opt==1) { f.add(x,y,v),t.add(x,v,1); if(y<n) t.add(y+1,v,1); } else { F<31> ret=t.ask(x+1,y,1); int al=f.get(x),ans; ret.insert(al); if(x==y) ans=max(v,v^al); else ans=ret.gmax(v); printf("%d\n",ans); } // for(int i=1;i<=n;i++) printf("a[%d]:%d\n",i,f.get(i)); } return 0; }
CF1100F Ivan and Burgers
求区间最大异或和
直接线段树暴力合并,
贪心,让线性基中后面维度的下标尽量大,求解就取出
我们记录线性基第
如果
inline int gmax(int v,int l){ int ans=v; for(int i=T;i>=0;i--) if((ans^b[i])>ans&&pos[i]>=l) ans^=b[i]; return ans; }
那怎么让
假设我们正插入
当
如果
防止存太多线性基
时间复杂度
口糊不太清楚,看代码更清晰-->
#include<bits/stdc++.h> #define ll long long #define SF scanf #define PF printf #define PB push_back #define cmax(x,y) x=max(x,y); #define cmin(x,y) x=min(x,y); #define ull unsigned long long #define R register #define rep(i,x,y) for(int i=x;i<=y;i++) #define IOS ios::sync_with_stdio(false) using namespace std; template<int T> struct F{ int b[T+2],pos[T+2]; F(){ for(int i=0;i<=T;i++) b[i]=pos[i]=0; } inline bool insert(int x,int y){ for(int i=T;i>=0;i--) if((x>>i)&1){ if(b[i]){ if(pos[i]<y) swap(b[i],x),swap(y,pos[i]); x^=b[i]; continue; } b[i]=x; pos[i]=y; return true; } return 0; } inline int gmax(int v,int l){ int ans=v; for(int i=T;i>=0;i--) if((ans^b[i])>ans&&pos[i]>=l) ans^=b[i]; return ans; } int operator [](int x){ return b[x]; } }; int n,m,a[500005],ans[500010]; F<21> p; struct ask{ int l,r,id; }c[500050]; inline bool cmp(ask a,ask b){ return a.r<b.r; } int main(){ cin>>m; for(int i=1;i<=m;i++) scanf("%d",&a[i]); cin>>n; for(int i=1;i<=n;i++) cin>>c[i].l>>c[i].r,c[i].id=i; sort(c+1,c+1+n,cmp); int r=0; for(int i=1;i<=n;i++){ while(r<c[i].r) p.insert(a[++r],r); ans[c[i].id]=p.gmax(0,c[i].l); } for(int i=1;i<=n;i++) printf("%d\n",ans[i]); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!