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

    bzoj3105 新Nim游戏

    题目大意:给定n堆火柴,第一二次取的时候可以取走任意堆,然后和普通的游戏一样,问怎样取能先手必胜,最小化第一次取的火柴数。

    思路:第一次取走后,剩下的火柴中不存在一个子集异或和为0(用线性基判断这件事),贪心能取就取,总和减去剩下的就可以了。实现的时候我们是用别的数来异或当前为上为1的那个最大数,而不是反之。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #define maxnode 105
    #define LL long long
    using namespace std;
    int a[maxnode]={0},ci[maxnode]={0},ins[50]={0};
    int main()
    {
        int k,i,j,p;LL sum=0,ans=0;
        scanf("%d",&k);
        for (i=1;i<=k;++i) scanf("%d",&a[i]);
        sort(a+1,a+k+1);
        for (i=1;i<=k;++i) sum+=(ci[i]=a[i]);
        for (i=k;i>=1;--i)
        {
            for (j=29;j>=0;--j)
                if (a[i]&(1<<j))
                {
                    if (!ins[j])
                    {
                        ins[j]=i;break;
                    }
                    else a[i]^=a[ins[j]];
                }
            if (a[i]) ans+=ci[i];
        }
        printf("%I64d
    ",sum-ans);
    }
    View Code

    bzoj2460 元素

    题目大意:给定n中矿石的编号和价值,求取出的矿石中不存在一个子集的编号异或和为0的最大价值。

    思路:同上一题,贪心,不过这里是取异或和不为0的,还有一点:左移右移是int,这里的long long范围,所以要写成右移的形式。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxnode 1005
    #define LL long long
    #define up 62
    using namespace std;
    struct use{
        int val;LL id;
    }ai[maxnode]={0};
    int ins[maxnode]={0};
    int cmp(const use&x,const use &y){return x.val>y.val;}
    int main()
    {
        int n,i,j,t,ans=0;scanf("%d",&n);
        for (i=1;i<=n;++i) scanf("%I64d%d",&ai[i].id,&ai[i].val);
        sort(ai+1,ai+n+1,cmp);
        for (i=1;i<=n;++i)
        {
            for (j=up;j>=0;--j)
                if ((ai[i].id>>j)&1)
                {
                    if (!ins[j]){ins[j]=i;break;}
                    else ai[i].id^=ai[ins[j]].id;
                }
            if (ai[i].id) ans+=ai[i].val;
        }
        printf("%d
    ",ans);
    }
    View Code

    bzoj2115 Xor(!!!

    题目大意:给定一张无向图,求从1~n的一条权值异或和最大的路径。

    思路:每一种路径都可以看作是直接的路径和一些环的组合,所以我们可以找出所有的环(并不一定是所有的,但是要找出足够组合出所有环的小环,所以可以dfs,如果v访问过就是环,同时这个环的异或和就是disu^disv^vauv)。找到所有环和它们的权值后,要用线性基的方式,贪心算出答案。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxm 50005
    #define maxe 200005
    #define LL long long
    using namespace std;
    int point[maxm]={0},next[maxe]={0},en[maxe]={0},tot=0;
    LL dis[maxm]={0},va[maxe]={0},hu[maxe]={0},ins[maxe]={0};
    bool visit[maxm]={false};
    void add(int u,int v,LL vv){
        next[++tot]=point[u];point[u]=tot;en[tot]=v;va[tot]=vv;
        next[++tot]=point[v];point[v]=tot;en[tot]=u;va[tot]=vv;
    }
    void dfs(int u){
        int i,j,v;visit[u]=true;
        for (i=point[u];i;i=next[i]){
            if (visit[v=en[i]]) hu[++hu[0]]=dis[u]^dis[v]^va[i];
            else{dis[v]=dis[u]^va[i];dfs(v);}
        }
    }
    void pre(){
        int i,j,x;
        for (i=1;i<=hu[0];++i){
            for (j=60;j>=0;--j)
                if ((hu[i]>>j)&1){
                    if (!ins[j]){ins[j]=i;break;}
                    else hu[i]^=hu[ins[j]];
                }
        }
    }
    int main(){
        int n,m,i,j,u,v;LL vv,ans;
        scanf("%d%d",&n,&m);
        for (i=1;i<=m;++i){
            scanf("%d%d%I64d",&u,&v,&vv);add(u,v,vv);
        }dfs(1);pre();ans=dis[n];
        for (i=60;i>=0;--i)
            if (!((ans>>i)&1)&&ins[i]) ans^=hu[ins[i]];
        printf("%I64d
    ",ans);
    }
    线性基
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxm 50005
    #define maxe 200005
    #define LL long long
    using namespace std;
    int point[maxm]={0},next[maxe]={0},en[maxe]={0},tot=0;
    LL dis[maxm]={0},va[maxe]={0},hu[maxe]={0};
    bool visit[maxm]={false};
    void add(int u,int v,LL vv){
        next[++tot]=point[u];point[u]=tot;en[tot]=v;va[tot]=vv;
        next[++tot]=point[v];point[v]=tot;en[tot]=u;va[tot]=vv;
    }
    void dfs(int u){
        int i,j,v;visit[u]=true;
        for (i=point[u];i;i=next[i]){
            if (visit[v=en[i]]) hu[++hu[0]]=dis[u]^dis[v]^va[i];
            else{dis[v]=dis[u]^va[i];dfs(v);}
        }
    }
    void pre(){
        int i,j,x,st=1;
        for (i=60;i>=0;--i){
            for (x=0,j=st;j<=hu[0];++j)
                if ((hu[j]>>i)&1){x=j;break;}
            if (x){
                for (j=1;j<=hu[0];++j)
                    if (j!=x&&((hu[j]>>i)&1)) hu[j]^=hu[x];
                swap(hu[x],hu[st]);++st;
            }
        }
    }
    int main(){
        int n,m,i,j,u,v;LL vv,ans;
        scanf("%d%d",&n,&m);
        for (i=1;i<=m;++i){
            scanf("%d%d%lld",&u,&v,&vv);add(u,v,vv);
        }dfs(1);pre();ans=dis[n];
        for (i=hu[0];i;--i) ans=max(ans,ans^hu[i]);
        printf("%lld
    ",ans);
    }
    gauss

    bzoj2728 与非(!!!

    题目大意:定义一种新操作:A nand B(真值表如下:1 1 0;0 1 1;1 0 1;0 0 1),问给定的n个数能通过这种运算得出[L,R]中的几个数。

    思路:nand操作可以代替位运算所有操作,根据位运算的性质可以得到如果一些位置的01状态在每个数中都一样,那么这些位置运算后的状态对应一样(比如0010和1101从后数的1、3位一样,所以最后运算出来的1、3位要么x1x1要么x0x0),我们通过求出这些关联位置可以得到一组线性基(作为一组线性基的数,对于某一位,它们二进制上只有一个1),求关联位置的方法是一些位运算技巧:从高到低枚举没有出现过的二进制位,枚举每一个数x,如果这一位是1,就&x,如果是0,就&~x,这样最后是1的位置就是关联位置了。考虑计算[1~x]中能算出来的数的个数,用之前的线性基,数位dp,考虑这一位是0/1(但这一部分有一种比较好写的办法,如果当前的线性基i|已经选的<=x,给答案加上2^(tot-i),表示这一个线性基不选,其他的有这么多种方案;然后选上这个线性基,再接着做)。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long
    #define N 1005
    using namespace std;
    LL ai[N],ji[N];int tot=0;
    bool vi[N]={false};
    LL calc(LL x){
        int i;LL ans=0,ci=0;
        if (x==-1LL) return x;
        for (i=1;i<=tot;++i)
          if ((ci|ji[i])<=x){
            ci|=ji[i];ans|=(1LL<<(tot-i));
          }return ans;}
    int main(){
        int n,i,j,k;LL l,r,now,cc;
        scanf("%d%d%I64d%I64d",&n,&k,&l,&r);
        for (i=1;i<=n;++i) scanf("%I64d",&ai[i]);
        for (cc=(1LL<<k)-1,i=k-1;i>=0;--i){
            if (vi[i]) continue;
            for (now=cc,j=1;j<=n;++j){
                if ((ai[j]>>i)&1LL) now&=ai[j];
                else now&=~ai[j]&cc;
            }ji[++tot]=now;
            for (j=0;j<=i;++j)
              if (now&(1LL<<j)) vi[j]=true;
        }printf("%I64d
    ",calc(r)-calc(l-1));
    }
    View Code

    bzoj4004 装备购买

    题目大意:给定n个装备,每个装备有m个属性值(看做一个m维向量),选一些装备,如果一个装备能被已选的某些装备线性表出就不能选。问选最多装备的最小代价。

    思路:可以贪心,所以就可以对m位用线性基做。(eps不能选的太小!!!)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 505
    #define LD double
    #define eps 1e-5
    using namespace std;
    struct use{
        LD m[N];int c;
        bool operator<(const use&x)const{return c<x.c;}
    }ai[N];
    int vi[N]={0};
    int cmp(LD x,LD y){
        if (x-y>eps) return 1;
        if (y-x>eps) return -1;
        return 0;}
    int main(){
        int n,m,i,j,q,ans=0,cnt=0;LD k;
        scanf("%d%d",&n,&m);
        for (i=1;i<=n;++i)
          for (j=1;j<=m;++j) scanf("%lf",&ai[i].m[j]);
        for (i=1;i<=n;++i) scanf("%d",&ai[i].c);
        sort(ai+1,ai+n+1);
        for (i=1;i<=n;++i){
            for (j=m;j;--j){
                if (cmp(ai[i].m[j],0.)==0) continue;
                if (!vi[j]){vi[j]=i;break;}
                else{
                    k=ai[i].m[j]/ai[vi[j]].m[j];
                    for (q=m;q;--q) ai[i].m[q]-=ai[vi[j]].m[q]*k;
                }
            }for (j=m;j;--j)
              if (cmp(ai[i].m[j],0.)!=0) break;
            if (j){++cnt;ans+=ai[i].c;}
        }printf("%d %d
    ",cnt,ans);
    }
    View Code

     
    bzoj4568 幸运数字

    题目大意:给一棵树,求某条路径上的某些点权的异或和最大。

    思路:倍增+线性基。倍增数组维护u向上2^i步的线性基,合并的时候暴力合并(因为线性基能表示出所有数,所以可以把一个线性基中的数看作所有数插入另一个)。查询的时候合并出链的线性基,贪心算答案。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 20005
    #define up 16
    #define M 60
    #define LL long long
    using namespace std;
    int point[N]={0},next[N<<1],en[N<<1],tot=0,fa[N][up],dep[N]={0};
    struct use{
        LL nm[M];
    }xj[N][up],ci[2];
    LL ai[N];
    int in(){
        char ch=getchar();int x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }return x;}
    LL lin(){
        char ch=getchar();LL x=0LL;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=x*10LL+(LL)(ch-'0');ch=getchar();
        }return x;}
    void add(int u,int v){
        next[++tot]=point[u];point[u]=tot;en[tot]=v;
        next[++tot]=point[v];point[v]=tot;en[tot]=u;}
    use merge(use x,use y){
        int i,j;use a;a=x;
        for (i=M-1;i>=0;--i){
            if (!y.nm[i]) continue;
            for (j=i;j>=0;--j){
                if (!((y.nm[i]>>j)&1)) continue;
                if (!a.nm[j]){a.nm[j]=y.nm[i];break;}
                else y.nm[i]^=a.nm[j];
            }
        }return a;}
    void pre(int u,int ff){
        int i,v;fa[u][0]=ff;dep[u]=dep[ff]+1;
        for (i=M-1;i>=0;--i)
            if ((ai[u]>>i)&1){xj[u][0].nm[i]=ai[u];break;}
        for (i=1;i<up;++i){
            fa[u][i]=fa[fa[u][i-1]][i-1];
            xj[u][i]=merge(xj[u][i-1],xj[fa[u][i-1]][i-1]);
        }for (i=point[u];i;i=next[i]){
            if ((v=en[i])==ff) continue;
            pre(v,u);
        }
    }
    LL calc(){
        int i;LL ans=0LL;
        for (i=M-1;i>=0;--i)
            if (!((ans>>i)&1)&&ci[0].nm[i]) ans^=ci[0].nm[i];
        return ans;}
    LL geta(int u,int v){
        int i;if (dep[u]<dep[v]) swap(u,v);
        memset(ci,0,sizeof(ci));
        for (i=up-1;i>=0;--i)
            if (dep[fa[u][i]]>=dep[v]){
                ci[0]=merge(ci[0],xj[u][i]);
                u=fa[u][i];
            }
        if (u==v){
            ci[0]=merge(ci[0],xj[u][0]);
            return calc();
        }for (i=up-1;i>=0;--i)
            if (fa[u][i]!=fa[v][i]){
                ci[0]=merge(ci[0],xj[u][i]);
                ci[1]=merge(ci[1],xj[v][i]);
                u=fa[u][i];v=fa[v][i];
            }
        ci[0]=merge(ci[0],xj[u][0]);
        ci[1]=merge(ci[1],xj[v][0]);
        u=fa[u][0];
        ci[0]=merge(merge(ci[0],xj[u][0]),ci[1]);
        return calc();}
    int main(){
        int n,m,i,u,v;n=in();m=in();
        for (i=1;i<=n;++i) ai[i]=lin();
        for (i=1;i<n;++i){u=in();v=in();add(u,v);}
        pre(1,0);
        for (i=1;i<=m;++i){
            u=in();v=in();
            printf("%I64d
    ",geta(u,v));
        }
    }
    View Code

    bzoj3569 DZY Loves Chinese II

    题目大意:给定一张连通图,q个询问,每次删去k(k<=15)条边,问图是否连通。

    思路:dfs出dfs树之后,如果树边和能覆盖这条树边的所有非树边同时删掉了,就是不连通。给非树边rand一个权值,树边的权值是所有覆盖它的非树边的权值的异或和,如果删掉边的权值中有一个子集异或和为0,就说明不连通(!!!)。

    注意:给树边赋值的时候可以差分,一开始竟然暴力用每条非树边赋值。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<ctime>
    #define N 100005
    #define M 1000005
    #define up 31
    using namespace std;
    struct use{int u,v,va,ir;}ed[M];
    int point[N],next[M],tot,ai[N],xj[up],bi[N]={0};
    bool vi[N]={false};
    int in(){
        char ch=getchar();int x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }return x;}
    void add(int u,int v){
        next[++tot]=point[u];point[u]=tot;ed[tot]=(use){u,v,0,0};
        next[++tot]=point[v];point[v]=tot;ed[tot]=(use){v,u,0,0};}
    void dfs(int u){
        int i,v;vi[u]=true;
        for (i=point[u];i;i=next[i]){
            if (vi[v=ed[i].v]) continue;
            ed[i].ir=ed[i^1].ir=1;
            dfs(v);
        }
    }
    void pre(int u){
        int i,v;vi[u]=true;
        for (i=point[u];i;i=next[i]){
            if (vi[v=ed[i].v]) continue;
            pre(v);
            ed[i].va^=bi[v];
            ed[i^1].va^=bi[v];
            bi[u]^=bi[v];
        }
    }
    bool judge(int x){
        int i,j;
        memset(xj,0,sizeof(xj));
        for (i=1;i<=x;++i){
            for (j=up-1;j>=0;--j){
                if (!((ai[i]>>j)&1)) continue;
                if (!xj[j]){xj[j]=ai[i];break;}
                ai[i]^=xj[j];
            }if (!ai[i]) return true;
        }return false;
    }
    int main(){
        int n,m,q,k,i,j,cnt=0,u,v;n=in();m=in();
        for (tot=i=1;i<=m;++i){u=in();v=in();add(u,v);}
        q=in();dfs(1);
        for (i=2;i<=tot;i+=2)
            if (!ed[i].ir){
                ed[i].va=ed[i^1].va=rand();
                bi[ed[i].u]^=ed[i].va;
                bi[ed[i].v]^=ed[i].va;
            }
        memset(vi,false,sizeof(vi));
        pre(1);
        for (i=1;i<=q;++i){
            k=in();
            for (j=1;j<=k;++j){
                u=in()^cnt;
                ai[j]=ed[u<<1].va;
            }if (judge(k)) printf("Disconnected
    ");
            else{printf("Connected
    ");++cnt;}
        }
    }
    View Code

    bzoj2844 albus就是要第一个出场

    题目大意:求子集异或和不去重排序后,x这个数最早出现的下标。

    思路:考虑求出每一位只有一个1的线性基共m个,有n个数,一共有2^n个异或和,2^m种异或和,每一种异或和中:对于那n-m个不作为线性基的数是可以随便取的,并且可以通过组合得到不同的异或和。所以每种异或和出现次数都是2^(n-m)(!!!)。这样就可以数位dp出<x的异或种数,*2^(n-m)+1就是答案了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 100005
    #define up 31
    #define p 10086
    using namespace std;
    int in(){
        char ch=getchar();int x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }return x;}
    int ai[N],xj[up]={0},bi[up]={0};
    int mi(int x,int y){
        int a=1;
        for (;y;y>>=1){
            if (y&1) a=a*x%p;
            x=x*x%p;
        }return a;}
    int main(){
        int n,i,j,q,x,ans=0;n=in();
        for (i=1;i<=n;++i){
            ai[i]=in();
            for (x=ai[i],j=up-1;j>=0;--j){
                if (!((x>>j)&1)) continue;
                if (!xj[j]){xj[j]=x;break;}
                else x^=xj[j];
            }
        }for (i=up-1;i>=0;--i)
            for (j=i-1;j>=0;--j)
                if ((xj[i]>>j)&1) xj[i]^=xj[j];
        q=in();
        for (i=j=0;i<up;++i)
            if (xj[i]) bi[i]=j++;
        for (i=up-1;i>=0;--i)
            if (((q>>i)&1)&&xj[i]){
                q^=xj[i];ans|=(1<<bi[i]);
            }
        printf("%d
    ",(ans%p*mi(2,n-j)+1)%p);
    }
    View Code
  • 相关阅读:
    为什么不使用CSS expression?
    关于ol有序列表的小事儿...
    绝对定位的元素在IE6下莫名丢失解决办法
    C#操作XML
    .NET MSChart应用的一个简单例子 (转)
    微软图表控件MsChart使用初探(转)
    使用OleDbParameter来写Access的更新没反应的解决办法
    获取真实IP
    XML操作类转
    Model与XML互相转换
  • 原文地址:https://www.cnblogs.com/Rivendell/p/4734691.html
Copyright © 2011-2022 走看看