[BZOJ4568]幸运数

线性基+倍增暴力维护

好难啊难啊啊

  • 给定一颗n个节点的树,每个点有一个权值\(a_i\),大小在\(2^{60}\)内,有q组询问,每次给定u,v,询问u到v的路径上的点的最大子集异或和。
  • 最开始想的是莫队能不能搞一下,但是线性基怎么删除了,所以不可行。
  • 那还能什么办法啊QAQ。处理树上路径问题,好用的就只剩倍增了吧。
  • 这时候你可能会有质疑了,倍增怎么优秀维护线性基啊?不急,不需要优秀,暴力往上怼,把你想到的暴力打上去。暴力求会吧?会吧?会吧?就是你每一个f【x】【i】维护一个线性基,倍增向上跳的时候直接暴力合并。那么思路就很清晰了,代码也很好写(至少没太多细节,不用犯sb错误)。
Coding
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e4+10;
const int MAXN=63;
int n,q,tot,ver[N<<1],Next[N<<1],lin[N],dep[N],fa[N][25];ll val[N],f[N][25][64],c[100];
void insert(ll *a,ll v){
    for(int i=MAXN;i>=0;--i) if((v>>i)&1){
        if(a[i]) v^=a[i];
        else{
            for(int j=i-1;j>=0;--j)
                if((v>>j)&1) v^=a[j];
            for(int j=i+1;j<=MAXN;++j)
                if((a[j]>>i)&1) a[j]^=v;
            a[i]=v;
            break;
        }
    }
}
void add(int x,int y){
    ver[++tot]=y;Next[tot]=lin[x];lin[x]=tot;
}
void merge(ll *a,ll *b){
    for(int i=0;i<=MAXN;++i) if(b[i]) insert(a,b[i]);
}
void dfs(int x,int ff){
    fa[x][0]=ff;dep[x]=dep[ff]+1;
    insert(f[x][0],val[ff]);
    for(int i=1;i<=20;++i){
        if((1<<i)<=dep[x]){
            fa[x][i]=fa[fa[x][i-1]][i-1];
            memcpy(f[x][i],f[x][i-1],sizeof(f[x][i]));
            merge(f[x][i],f[fa[x][i-1]][i-1]);
        }
    }
    for(int i=lin[x];i;i=Next[i]){
        int y=ver[i];
        if(y==ff) continue;
        dfs(y,x);
    }
}
void lca(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    insert(c,val[x]);insert(c,val[y]);
    for(int i=20;i>=0;--i){
        if(dep[fa[x][i]]>=dep[y]){
            merge(c,f[x][i]);x=fa[x][i];
        }
    }
    if(x==y) return;
    for(int i=20;i>=0;--i){
        if(fa[x][i]!=fa[y][i]){
            merge(c,f[x][i]);
            merge(c,f[y][i]);
            x=fa[x][i],y=fa[y][i];
        }
    }
    insert(c,val[fa[x][0]]);
}
int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;++i) scanf("%lld",&val[i]);
    for(int i=1;i<n;++i){
        int x,y;scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }dep[0]=-1;
    dfs(1,0);
    for(int i=1;i<=q;++i){
        memset(c,0,sizeof(c));
        int x,y;scanf("%d%d",&x,&y);
        lca(x,y);
        ll ans=0;
        for(int i=MAXN;i>=0;--i) ans=max(ans,ans^c[i]);
        printf("%lld\n",ans);
    }
    return 0;
}
speech.gif posted on 2019-02-24 16:22 kgxpbqbyt 阅读( ...) 评论( ...) 编辑 收藏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值