https://www.cnblogs.com/downrainsun/p/11228690.html
完成套路:往自己这里搬。
性质:
设数集T的值域范围为[1,2^n−1]。
T的线性基是T的一个子集A={a1,a2,a3,...,an}。
A中元素互相xor所形成的异或集合,等价于原数集T的元素互相xor形成的异或集合。
可以理解为将原数集进行了压缩。
性质
1.设线性基的异或集合中不存在0。
2.线性基的异或集合中每个元素的异或方案唯一,其实这个跟性质1是等价的。
3.线性基二进制最高位互不相同。
4.如果线性基是满的,它的异或集合为[1,2^n−1]。
5.线性基中元素互相异或,异或集合不变。
接下来是题目:
一,洛谷彩灯
给你m个开关,每个开关表示可以控制哪些灯的状态,且状态取反,亮则暗,暗则亮,求多少种不重复的灯亮状态
题解:首先可以知道的是那些开关实际上就是异或操作,那么考虑到不重复,那就想到线性基
线性基性质A中元素互相xor所形成的异或集合,等价于原数集T的元素互相xor形成的异或集合。,同时不出现重复,那接下来就好办了,成功解决不重复问题,就算线性基有多少个数存在,然后2的个数次方就完事
#include <cstdio> #include <cstring> #define ll long long int n,m; ll d[60]; char s[60]; void add(ll x) { for(int i=50;i>=0;i--) { if(x&(1ll<<i)) { if(d[i]==0) { d[i]=x; return; } else x^=d[i]; } } } int main() { scanf("%d %d",&n,&m); for(int i=1;i<=m;i++) { scanf("%s",s+1); ll x=0; for(int j=1;j<=n;j++) if(s[n-j+1]=='O')x^=(1ll<<(j-1)); add(x); } int ans=0; for(int i=0;i<=50;i++) if(d[i]!=0)ans++; printf("%lld",(1ll<<ans)%2008); }
luogu:最大xor和路径
大佬讲解:https://www.luogu.org/blog/An-Amazing-Blog/solution-p4151
我的代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN=200005; struct enode{int to; ll c;}; bool vis[MAXN]; int num=0,n,m; ll s[MAXN],g[MAXN]; vector<enode> e[MAXN]; ll read() { ll f=1,x=0; char c=getchar(); while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); } while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+c-'0'; c=getchar(); } return x*f; } void dfs(int x,int father) { //cout<<x<<" "<<s[x]<<endl; vis[x]=1; for (int i=0;i<e[x].size();i++) if (!vis[e[x][i].to]) { s[e[x][i].to]=s[x]^e[x][i].c; // printf("%d %d\n",e[x][i].to,s[e[x][i].to]); dfs(e[x][i].to,x); } else if (e[x][i].to!=father) g[++num]=s[x]^s[e[x][i].to]^e[x][i].c; } struct Xor_Basis { int maxsz; ll basis[65]; void init(){ memset(basis,0,sizeof basis); maxsz=63;} bool insert(ll x) { for (int i=maxsz;i>=0;i--) if ((x>>i)&1) if (basis[i]) x^=basis[i]; else { basis[i]=x; break; } return x; } }QAQ; int main() { QAQ.init(); n=read(),m=read(); for (int i=1;i<=m;i++) {x int u=read(),v=read();ll c=read(); e[u].push_back((enode){v,c}); e[v].push_back((enode){u,c}); } dfs(1,0); for (int i=1;i<=num;i++) {QAQ.insert(g[i]);} ll ans=s[n]; for (int i=QAQ.maxsz;i>=0;i--) if ((ans^QAQ.basis[i])>ans) ans^=QAQ.basis[i]; printf("%lld\n",ans); return 0; }
cf的http://codeforces.com/problemset/problem/724/G:
https://blog.csdn.net/u013534123/article/details/79914683
题意其实就是一个u到v的多条路径每条路径的值是边的异或和,贡献是这些多条路径的和,当然重复的不算,因为题目规定的是三元组(u,v,s)。题中要注意的是图是不一定连在一起的,也就是可能有多个连分块。
好的,接下来题解:
简单暴力两个点肯定直接扑街;
我们知道u到v,肯定是由一条路径+若干个环组成(可见上面的大佬讲解博客);
所以,我们按位来计算贡献,一个位要产生贡献,那么该位最后肯定得是1,(因为本来是0的话,就是没有值,,莫得贡献),所以我们有以下两个方案
当一个起点u和终点v的i位相同,(这里的起点终点是1到u路径与1到v的路径),因为你算u到v肯定是这两个路径异或嘛,所以如果这两个i位相同也就是木有贡献,我们要让这个i位有贡献,就让他经过一个环,环我们已经处理好丢到线性基里了
然后我们找线性基里有没有i位位1的,有的话同时他只有一个(线性基性质),(再次解释下线性基可以保证不会出现重复方案且0的方案),接下来在线性基其他位就可以生成选或不选方案(正因为线性基不会重复,所以我们可以直接2的r-1次方,-1是因为那个i位为1的已经被选了);
那终点和起点怎么弄呢,没错,每次我们对这些路径进行每一位的计算,cnt【0】(i位为0)有多少个,cnt[1]有多少个。
然后组合数tmp=cnt[0]*(cnt[0]-1)/2+cnt[1]*(cnt[1]-1)/2;
接下来是当起点和终点不一样时的方案:、起点和终点第i位不同,这意味着中间不能经过第i位位1的环。还是一样,看是否有这样的环。如果有,则除了这个环不能取,其他任意,对应2^(r-1)种方案;如果没有那么所有r个向量都可以任意,对应2^r种方案。
记住,我们刚才计算的只是方案数tmp,所以我们更新答案就得再乘上2^i,因为在i位上表示的就是这个值的贡献嘛,所以你的方案数得乘上值才是答案。
#include<bits/stdc++.h> #define mod 1000000007 #define LL long long #define N 500010 using namespace std; struct Linear_Basis { LL b[63],nb[63],tot; void init() { tot=0; memset(b,0,sizeof(b)); memset(nb,0,sizeof(nb)); } bool ins(LL x) { for(int i=62;i>=0;i--) if (x&(1LL<<i)) { if (!b[i]) {b[i]=x;break;} x^=b[i]; } return x>0; } LL Max(LL x) { LL res=x; for(int i=62;i>=0;i--) res=max(res,res^b[i]); return res; } LL Min(LL x) { LL res=x; for(int i=0;i<=62;i++) if (b[i]) res^=b[i]; return res; } void rebuild() { for(int i=62;i>=0;i--) for(int j=i-1;j>=0;j--) if (b[i]&(1LL<<j)) b[i]^=b[j]; for(int i=0;i<=62;i++) if (b[i]) nb[tot++]=b[i]; } LL Kth_Max(LL k) { LL res=0; for(int i=62;i>=0;i--) if (k&(1LL<<i)) res^=nb[i]; return res; } } LB; LL s[N],w[N],loop[N],cnt[2],ans; struct Edge{int y;LL w;}; vector<Edge> g[N]; vector<int> p; int n,m,r,tot; bool v[N]; void dfs(int x) { v[x]=1; for(int i=0;i<g[x].size();i++) { int y=g[x][i].y; LL w=g[x][i].w; if (!v[y]) { s[y]=s[x]^w,dfs(y); p.push_back(y); } else loop[++tot]=s[y]^s[x]^w; } } LL qpow(LL a,LL b) { LL ans=1; while(b) { if(b&1)ans=ans*a%mod; a=a*a%mod; b>>=1; } return ans; } void calc() { for(int i=0;i<=62;i++) { bool flag=0; cnt[0]=cnt[1]=0; for(int j=0;j<p.size();j++) cnt[(s[p[j]]>>i)&1]++; LL tmp=cnt[0]*(cnt[0]-1)/2+cnt[1]*(cnt[1]-1)/2; tmp%=mod;// //起点终点第i位相同的方案数 // for(int j=0;j<=62;j++) if (LB.b[j]&(1LL<<i)) {flag=1;break;}// //判断是否有向量第i位为1 for(int j=0;j<=62;j++) if ((LB.b[j]>>i)&1) {flag=1;break;}// //判断是否有向量第i位为1 if (flag) { if (r) tmp=tmp*qpow(2,r-1)%mod;// //如果有那么统计 ans=(ans+tmp*qpow(2,i)%mod)%mod; } tmp=cnt[0]*cnt[1]%mod;// //起点和终点第i为不同的方案数 if (flag)// { if (r) tmp=tmp*qpow(2,r-1)%mod; } else tmp=tmp*qpow(2,r)%mod; ans=(ans+tmp*qpow(2,i)%mod)%mod; } } int main() { while(~scanf("%d%d",&n,&m)) { ans=0; for(int i=1;i<=m;i++) { int u,v; LL p; scanf("%d%d%lld",&u,&v,&p); g[u].push_back(Edge{v,p}); g[v].push_back(Edge{u,p}); } for(int i=1;i<=n;i++) if (!v[i]) { LB.init(); p.clear();// // //不同连通分量的东西不能够混用 tot=r=0; p.push_back(i); dfs(i); for(int j=1;j<=tot;j++) LB.ins(loop[j]); for(int j=0;j<=62;j++) if (LB.b[j]) r++; calc(); } printf("%lld\n",ans); } }