好久都没有更过知识点了
- 引入:(可以跳过)
首先可以看一下这道题(高斯消元会的情况下):
然后就会有一个基的概念(虽然我也不能太理解)。 - 概念:(解决异或问题)
不过线性基就是给定原数列,构建一个新数列,满足原数列的异或和集合=新数列的异或和集合,而且新数列大小尽量小。 - 性质:
1.集合里按照存储,bs[i]如果不为0,它的最高位一定是。
2.理解:每个insert(y)结束后y都会变为0(说明其中肯定能异或出y)
3.集合里没有异或和为0的数,判断有没有0可以用性质2 - code
1.插入值:
bool Insert(int y) {
for(int i=60;i>=0;i--) {
if((y>>i)&1) {
if(!Bs[i]){Bs[i]=y;return;} //找到了就return,不要忘了
else y^=Bs[i];
}
}
}
2.与k异或最大
ll Mx(ll k) {
for(int i=60;i>=0;i--) {
bool val=(k>>i)&1;
if(!val) {k^=Bs[i];}
}
return k;
}
3.任意异或最大值
int mx=0;
for(int i=up;i>=0;i--) {
if((mx>>i)&1) continue;
mx^=Bs[i];
}
ps.最小值就先判断有没有异或和=0的,没有就最低位的大于0的bs[i]。
4.第k大:
转化为D[]类线性基,性质:
1.(第i位)只会在一个D[]的值中存在
2.两个bs[]异或起来一定会大于其中任意一个的值
操作方法,枚举每个bs[i]的每一位,如第j位为1,就看bs[j]>0吗。最后再(把有值的)离散化一下。
所以会发现第k大,刚好就是它二进制位i为1的的Bs[i]异或和。
证明?感性:k=1时就是,k=2时显然每次你都会+1。
理性:单调性可以证明,因为性质1,只要找到第一个字典序严格大于的二进制位置就可以高低立判了。
ps.还需判断0
void re_build() {
for(int i=60;i>=0;i--) {
for(int j=i-1;j>=0;j--) {
if((Bs[i]>>j)&1) Bs[i]^=Bs[j];
}
}
for(int i=0;i<=60;i++)if(Bs[i])D[cnt++]=Bs[i];
}
补充:线性基求异或最短路
最后的路径一定是多个环和一条链连在一起的情况。
初始选择任意一条到的链,都能通过异或上若干环得到所有可能的情况。(证明:两条路径可以通过异或上两条路径共同的环相互转换,所以可以先得到路径,然后之后得到套的环直接加上去即可)
思路就转化为:随便找一条路径,然后求任意异或上环所得的最大值,找到每个环存在线性基里即可。找环可以用带权并查集或者dfs。(并查集做法可以结合可撤销联合出题)
- code
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
const int M=61;
typedef long long ll;
int fa[N],n,m,nxt[N],to[N],head[N],ecnt;
ll dis[N],len[N],Bs[M],fs[N];
bool vis[N];
void Insert(ll y) {
if(!y)return;
for(int i=60;i>=0;i--) {
if((y>>i)&1) {
if(!Bs[i]){Bs[i]=y;return;}
else y^=Bs[i];
}
}
}
ll Mx(ll k) {
for(int i=60;i>=0;i--) {
bool val=(k>>i)&1;
if(!val) {k^=Bs[i];}
}
return k;
}
void add_edge(int u,int v,ll w) {nxt[++ecnt]=head[u];to[ecnt]=v;len[ecnt]=w;head[u]=ecnt;}
int g_fa(int u) {
if(u==fa[u])return u;
int fx=g_fa(fa[u]);
fs[u]^=fs[fa[u]];
return fa[u]=fx;
}
void fd_Rd() {
for(int i=1;i<=n;i++)fa[i]=i,fs[i]=0;
for(int u=1;u<=n;u++) for(int j=head[u];j;j=nxt[j]) {
int v=to[j];
int fx=g_fa(u),fy=g_fa(v);
if(fx==fy) {Insert(len[j]^fs[u]^fs[v]);}
else {fa[fx]=fy;fs[fx]=fs[v]^len[j]^fs[u];}
}
}
queue<int> Q;
void BFS(int s) {
Q.push(s);vis[s]=1;
while(!Q.empty()) {
int u=Q.front(); Q.pop();
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
if(vis[v])continue;vis[v]=1;
dis[v]=dis[u]^len[i];Q.push(v);
}
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) {int u,v;ll w;scanf("%d%d%lld",&u,&v,&w);add_edge(u,v,w),add_edge(v,u,w);}
fd_Rd();
BFS(1);
printf("%lld\n",Mx(dis[n]));
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
2021-02-16 poj1475 -- Pushing Boxes