线性基

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);
    }
}

  

原文地址:https://www.cnblogs.com/hgangang/p/11783101.html