zoukankan      html  css  js  c++  java
  • 线性基

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

      

  • 相关阅读:
    json&display
    postgresql AutoVacuum系统自动清理进程
    postgresql vacuum操作
    C++ 在.h文件中包含头文件和在.cpp文件中包含头文件有什么区别
    ResetEvent、CreateEvent、SetEvent
    《转载》C语言的移位操作符
    《转载》如何使用M6117D看门狗定时器复位系统
    《转载》 Bit,Byte,WORD,DWORD区别和联系
    $.messager.alert
    对一个或多个实体的验证失败
  • 原文地址:https://www.cnblogs.com/hgangang/p/11783101.html
Copyright © 2011-2022 走看看