线性基
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);
}
}
浙公网安备 33010602011771号