[BZOJ4568][Scoi2016]幸运数字 树链剖分+线性基

学习了一下线性基这个东西
log时间支持加法
树链剖分维护一下
再利用线性基查询最大值即可
线性基博文推荐:http://www.cnblogs.com/ljh2000-jump/p/5869991.html

/**************************************************************
    Problem: 4568
    User: di4CoveRy
    Language: C++
    Result: Accepted
    Time:38544 ms
    Memory:185392 kb
****************************************************************/

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>

#define LOG 17
#define N 20050

using namespace std;
typedef long long LL;
int Jump[N][LOG+1],D[N],n,q;
LL F[N][LOG+1][64],a[N],ret[64];
bool vis[N];
vector<int> e[N];

inline void merge(LL *B,LL p) {
    for (int _=63;_>=0;_--) if (p>>_) {
        if (B[_]) p ^= B[_]; else return B[_] = p,(void)0;
    }
}
inline void _merge(LL *A,LL *B) {
    for (int _=0;_<=63;_++) if (B[_]) merge(A,B[_]);
}

void dfs(int u) {
    vis[u] = 1; 
    for (int i=0;i<(int)e[u].size();i++) {
        int v = e[u][i]; if (vis[v]) continue;
        D[v] = D[u] + 1;
        Jump[v][0] = u; 
        if (a[u]) F[v][0][63-__builtin_clz(a[u])] = a[u];
        dfs(v);
    }
}

void _pre() {
    for (int i=1;i<=LOG;i++)
        for (int _=1;_<=n;_++) {
            int tmp = Jump[_][i-1];
            Jump[_][i] = Jump[tmp][i-1];
            _merge( F[_][i] , F[_][i-1] );
            _merge( F[_][i] , F[tmp][i-1] );
        }
}

void LCA(int u,int v) {
    merge(ret,a[u]) , merge(ret,a[v]);
    if (D[u] < D[v]) swap(u,v);
    for (int i=LOG;i>=0;i--) if (D[ Jump[u][i] ] >= D[v])
        _merge(ret,F[u][i]) , u = Jump[u][i];
    if (u == v) return ;
    for (int i=LOG;i>=0;i--) if (Jump[u][i] != Jump[v][i])
        _merge(ret,F[u][i]) , u = Jump[u][i],
        _merge(ret,F[v][i]) , v = Jump[v][i];
    _merge(ret,F[u][0]);
}

int main() {
    scanf("%d%d",&n,&q);
    for (int _=1;_<=n;_++) scanf("%lld",&a[_]);
    for (int _=1;_<=n-1;_++) {
        int u,v; scanf("%d%d",&u,&v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    D[1]=1; dfs(1); _pre();
    while (q--) {
        memset(ret,0,sizeof(ret));
        int u,v; scanf("%d%d",&u,&v); 
        LCA(u,v);
        LL ans = 0;
        for (int _=63;_>=0;_--) if ((ans^ret[_]) > ans) ans ^= ret[_];
        printf("%lld\n",ans);
    }
    return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值