蒟蒻的 线性基 刷题记录

HDU3949 XOR

  • 大意:给出n个数,求这些数通过xor能得到的第k小的值
  • 做法:先高斯消元,完了之后回代,然后第k小,k用二进制表示,是1的那位就选,是0 的就不选
  • 注意:给出的n个数能不能xor出0?因为题目是不允许不选,所以要考虑0的情况。
  • 代码:
    #include <bits/stdc++.h>
    #define nmax 10010
    
    using namespace std;
    typedef long long ll;
    ll a[nmax],b[70],k[70]; //k[i] 第i小的数
    int n,q;
    
    int main(){
        int T;
        cin>>T;
        for (int cas=1; cas<=T; cas++) {
            memset(b,0,sizeof(b));
            memset(k,0,sizeof(k));
            printf("Case #%d:\n",cas);
            scanf("%d",&n);
            for (int i=0; i<n; i++) {
                scanf("%lld",&a[i]);
                //求线性基
                for (int j=61; j>=0; j--) {
                    if(a[i]&(1LL<<j)){
                        if(b[j]) a[i]^=b[j];
                        else { b[j]=a[i]; break; }
                    }
                }
            }
            //往上回代,顺便统计个数
            int cnt=0;
            for (int i=0; i<=61; i++) if(b[i]) {
                    k[cnt]=b[i];
                    cnt++;
                    for (int j=i+1; j<=61; j++) if(b[j]&(1LL<<i)) b[j]^=b[i];
            }
            ll mmax=(1LL<<cnt)-1;
            scanf("%d",&q);
            //cout<<"q= "<<q<<endl;
            ll inq;
            bool flag=false;//特别判断0
            if(cnt<n) flag=true;
            for (int j=0; j<q; j++) {
                   // cout<<"quq"<<endl;
                scanf("%lld",&inq);
                if(flag) inq--;  //也是判断0的内容
                if(inq>mmax) cout<<-1<<endl;
                else {
                    ll ans=0;
                    for (int i=0; (1LL<<i)<=inq; i++) if((1LL<<i)&inq) ans^=k[i];
                    printf("%lld\n",ans);
                }
            }
        }
        return 0;
    }

    BZOJ2460 元素

  • 做法:贪心,按magic值从大到小排序求线性基
  • 代码:
    #include <bits/stdc++.h>
    #define nmax 1010
    
    using namespace std;
    typedef long long ll;
    struct stone{
        ll num,mac;
        bool operator < (const stone a) const { return a.mac<mac; }
    }st[nmax];
    int n;
    ll b[70]={0};
    
    int main(){
        cin>>n;
        for (int i=0; i<n; i++) scanf("%lld%lld",&st[i].num,&st[i].mac);
        sort(st,st+n);
        //for (int i=0; i<n; i++) cout<<st[i].num<<' '<<st[i].mac<<endl;
        ll ans=0;
        for (int i=0; i<n; i++) for (int j=63; j>=0; j--) if(st[i].num&(1LL<<j)){
            if(b[j]) st[i].num^=b[j];
            else{
                b[j]=st[i].num;
                ans+=st[i].mac;
                break;
            }
        }
        printf("%lld\n",ans);
        return 0;
    }

     

 BZOJ2115 XOR

  • 做法:dfs先把图拉成一棵树,然后看有没有倒回去的边(类似tarjan那种)处理出所有的环的xor值,然后和1~ndfs树上那个值做线性基求出ans
  • 注意:wa的地方有两点,一个是处理环的时候,是不是环要看是不是dfs中“指回去”,用一个数组记录dfs的顺序。另一个是求最大xor值时。1~n那条路不要加到线性基里去,因为那个数是必须要取的,加到线性基里后面求最大值的时候可能就不取它了。
  • 代码:
     1 #include <bits/stdc++.h>
     2 #define nmax 50010
     3 #define mmax 100010
     4 
     5 using namespace std;
     6 typedef long long ll;
     7 int head[nmax]={0},d[nmax]={0};  //被vis到的顺序
     8 ll a[nmax+mmax],tmp[nmax],b[70]={0};
     9 int n,m,idx=0,idxa=0;  //idza是记录环的数量
    10 struct edge{
    11     int v,ne;
    12     ll w;
    13 }e[mmax*2];
    14 
    15 inline void addedge(int u,int v,ll w){
    16     idx++;
    17     e[idx].v=v;
    18     e[idx].w=w;
    19     e[idx].ne=head[u];
    20     head[u]=idx;
    21 }
    22 
    23 void build(){
    24     cin>>n>>m;
    25     int a,b;   ll c;
    26     for (int i=0; i<m; i++) {
    27         scanf("%d%d%lld",&a,&b,&c);
    28         addedge(a,b,c);
    29         addedge(b,a,c);
    30     }
    31 }
    32 
    33 void dfs(int u,int cnt){
    34     d[u]=cnt;
    35     for (int i=head[u]; i; i=e[i].ne){
    36         int v=e[i].v;  ll w=e[i].w;
    37         if(d[v]>d[u]) continue;
    38         if(d[v]){
    39             ll x=tmp[v]^tmp[u]^w;  //这个环的xor值
    40             if(x) a[++idxa]=x;  //如果是0就舍弃
    41         }else {
    42             tmp[v]=tmp[u]^w;  //在dfs(u)之前,u的tmp值已经算出
    43             dfs(v,cnt+1);
    44         }
    45     }
    46 }
    47 
    48 void xxj(){
    49     for (int i=1; i<=idxa; i++) for (int j=63; j>=0; j--) if((1LL<<j)&a[i]){
    50         if(b[j]) a[i]^=b[j];
    51         else { b[j]=a[i]; break; }
    52     }
    53     ll ans=tmp[n];
    54     for (int i=63; i>=0; i--) ans=max(ans,ans^b[i]);
    55     printf("%lld\n",ans);
    56 }
    57 
    58 int main(){
    59     build();
    60     dfs(1,1);
    61     xxj();
    62     return 0;
    63 }

 

BZOJ2844: albus就是要第一个出场

  • 对于给定的大小为N数列a,它的子集有2^n个,每个子集可以异或的所有的数可以用线性基表示
  • 设线性基有b个,然后不在线性基里的数有x=n-b个
  • 可以知道通过a任意异或出的所有数是这样一个情况:一共有2^b个不同的数,每个数出现的次数相同为2^x
  • 证明:把a分成两份,线性基的一份和不在线性基的一份,然后在不在线性基的那一份里随便取一种排列并把它们异或,设结果为v,v用线性基那一份也可以造出来,然后v^v=0,然后再用线性基造一遍所有的数,这些数就又全部出现了一次,这样搞可以让这些数全部出现2^x次
  • 代码:
    #include <bits/stdc++.h>
    #define nmax 100010
    #define mod 10086
     
    using namespace std;
    typedef long long ll;
    ll a[nmax],b[60];
    ll n,x;
     
    ll fastpow(ll x){
        ll ta=2,ans=1;
        for (ll i=1; i<=x; i<<=1){
            if(x&i) ans=(ans*ta)%mod;
            ta=(ta*ta)%mod;
        }
        return ans;
    }
     
    int main(){
        scanf("%lld",&n);
        for (ll i=0; i<n; i++) {
            scanf("%lld",&a[i]);
            for (ll j=40; j>=0; j--){
                if( (1LL<<j)&a[i] )
                    if(b[j]) a[i]^=b[j];
                    else { b[j]=a[i]; break; }
            }
        }
        scanf("%lld",&x);
        int k=0,cnt=0;
        for (ll i=0; i<=40; i++) {
            if(b[i]) {
          if( x & (1<<i) ) k+=(1LL<<cnt);//注意这里加的不是b[i]。。
                cnt++;
            }
        }
        cout<<k*fastpow(n-cnt)%mod+1<<endl;
        return 0;
    }    

     

posted @ 2019-07-26 12:59  连昵称都不能重复  阅读(186)  评论(0编辑  收藏  举报