BZOJ4568: [Scoi2016]幸运数字

本文介绍了解决BZOJ4568:[Scoi2016]幸运数字问题的方法,通过使用线性基、倍增技术和LCA算法来求解抑或和最大值问题。具体实现上,采用倍增方式维护线性基,并通过合并操作得出最终答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

BZOJ4568: [Scoi2016]幸运数字

线性基·倍增·LCA

题解:

抑或和最大的问题显然要用到线性基。

本题就直接倍增维护线性基,合并出答案即可。

线性基的合并就是一个插入到另一个中。

有一点小细节就是lb[i][j]中维护的是从i到i的2j级祖先的线性基,左开右闭,也就是不包括i本身的。因此查lca(a,b)的时候先把a和b的插入进去。

Code:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long LL;
const int N = 20005;
const int LOG = 61;

int n,q; LL g[N];
int dep[N],pa[N][21];

struct Edge{
    int to,next;
} e[N*2];
int head[N], ec;
void add(int a,int b){
    ec++; e[ec].to=b; e[ec].next=head[a]; head[a]=ec;
}

void input(){
    cin>>n>>q;
    for(int i=1;i<=n;i++) scanf("%lld",g+i);
    int a,b;
    for(int i=1;i<n;i++){
        scanf("%d%d",&a,&b);
        add(a,b); add(b,a);
    }
}

struct LB{
    LL d[LOG+1];
    LB(){ memset(d,0,sizeof(d)); }
    void insert(LL x){
        for(int j=LOG;j>=0;j--){
            if((x>>j)&1){
                if(!d[j]){ d[j]=x; break; }
                x^=d[j];
            }
        }
    }
    LL getMax(){
        LL x=0;
        for(int i=LOG;i>=0;i--){
            if((x^d[i])>x) x^=d[i];
        }
        return x;
    }
} lb[N][21];

void un(LB &a,const LB &b){
    for(int i=0;i<=LOG;i++){
        if(b.d[i]) a.insert(b.d[i]);
    }
}

void dfs(int u,int f){
    dep[u]=dep[f]+1; pa[u][0]=f; lb[u][0].insert(g[f]);
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==f) continue;
        dfs(v,u);
    }
}

void init(){
    for(int j=1;j<=20;j++){
        for(int i=1;i<=n;i++){
            pa[i][j]=pa[pa[i][j-1]][j-1];
            un(lb[i][j],lb[i][j-1]);
            un(lb[i][j],lb[pa[i][j-1]][j-1]);
        }
    }
}

LB LCA(int a,int b){
    LB res; res.insert(g[a]); res.insert(g[b]); 
    if(dep[a]<dep[b]) swap(a,b);
    int cha=dep[a]-dep[b];
    for(int j=20;j>=0;j--){
        if((cha>>j)&1){
            un(res,lb[a][j]);
            a=pa[a][j];
        }
    }
    if(a!=b){
        for(int j=20;j>=0;j--){
            if(pa[a][j]!=pa[b][j]){
                un(res,lb[a][j]);
                un(res,lb[b][j]);
                a=pa[a][j]; b=pa[b][j];
            }
        }
        un(res,lb[a][0]);
        a=pa[a][0];
    }
    return res;
}

void solve(){
    int a,b;
    while(q--){
        scanf("%d%d",&a,&b);
        LB res=LCA(a,b);
        printf("%lld\n",res.getMax());
    }
}

int main(){
    freopen("a.in","r",stdin);
    input();
    dfs(1,0);
    init();
    solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值