2019牛客暑期多校训练营(第四场)—— xor(线性基的交 线段树)

原题: https://ac.nowcoder.com/acm/contest/884/B

题意:

给出n个集合,m个查询,每个查询 ( L , R , v a l ) (L,R,val) (L,R,val),询问区间 [ L , R ] [L,R] [L,R]中的所有集合是否可以通过集合内异或来表示 v a l val val

解析:

异或表示显然就是线性基了,一个线性基是否可以表示一个数大家都会做,现在要求区间内所有线性基是否可以。

先来说一个很显然的东西,我们定义两个线性基的交为:两个基都可以表示的位。

我在之前讲过,线性基里面的每个基相当于二进制中的一个比特位,假设我第一个线性基可以表示 30 , 28 , 26 , 20 30,28,26,20 30,28,26,20,第二个 29 , 28 , 20 , 19 29,28,20,19 29,28,20,19,那么两个线性基的交就是 28 , 20 28,20 28,20

那么这道题显然考察的就是线性基的交,我们只需要求出一个区间内所有线性基的交,再用这个线性基去判断即可。如果交可以表示,那么显然区间内的所有线性基都可以表示了。

这个过程可以用线段树去维护,每个节点存对应区间的所有线性基的交。

代码:

#include
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
struct linear_Bace;
typedef linear_Bace LB;
const int maxn=50000+9;
const int Len=33;

struct linear_Bace{
    int a[Len];
    int& operator [](int idx){
        return a[idx];
    }
    int operator [](int idx)const{
        return a[idx];
    }
    void insert(int val){
        for(int i=Len-1;i>=0;i--){
            if((val>>i)&1){
                if(!a[i]){
                    a[i]=val;break;
                }
                val^=a[i];
            }
        }
    }
    bool find(int val){
        for(int i=Len-1;i>=0;i--){
            if((val>>i)&1){
                if(a[i]){
                    val^=a[i];
                }
                else
                    return 0;
            }
        }
        return 1;
    }
};
LB merge(LB a,LB b){
    LB ans=a;
    for(int i=Len-1;i>=0;i--){
        if(b[i]==0)continue;
        ans.insert(b[i]);
    }
    return ans;
}
LB intersect(LB a,LB b){
    LB ans= {},c=b,d=b;
    for(int i=0;i<Len;i++) {
        int x=a[i];
        if(!x)
            continue;
        int j=i;
        int T=0;
        for(; j>=0; --j){
            if((x>>j)&1)
                if(c[j]) {
                    x^=c[j];
                    T^=d[j];
                } else
                    break;
        }

        if(!x)
            ans[i]=T;
        else {
            c[j]=x;
            d[j]=T;
        }
    }
    return ans;
}

LB arr[maxn];
LB tr[maxn<<2];
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid (l+r>>1)

void build(int rt,int l,int r){
    if(l==r){
        tr[rt]=arr[l];
        return ;
    }
    build(ls,l,mid);
    build(rs,mid+1,r);
    tr[rt]=intersect(tr[ls],tr[rs]);
}

bool query(int rt,int l,int r,int L,int R,int val){
    if(l>=L&&r<=R){
        return tr[rt].find(val);
    }
    if(L<=mid)
        if(!query(ls,l,mid,L,R,val))return 0;
    if(R>mid)
        if(!query(rs,mid+1,r,L,R,val))return 0;
    return 1;
}

int main(){
    int n,q;scanf("%d%d",&n,&q);
    rep(i,1,n){
        int k;scanf("%d",&k);
        while(k--){
            int val;scanf("%d",&val);
            arr[i].insert(val);
        }
    }
    build(1,1,n);
    while(q--){
        int l,r,val;scanf("%d%d%d",&l,&r,&val);
        printf("%s\n",query(1,1,n,l,r,val)?"YES":"NO");
    }
}



你可能感兴趣的:(其他算法)