[SCOI2016]幸运数字

题目

来写一个\(3\)\(log\)的垃圾做法

其实非常显然就是倍增把这条路径处理一遍,之后维护出倍增数组的线性基,大力合并就好了

线性基合并就是把一个线性基的所有元素都拿出来,一个一个插入到另外一个中去

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 20005
#define re register
#define LL long long
inline LL read() {
    LL x=0;char c=getchar();while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
struct E{int v,nxt;}e[maxn<<1];
int f[maxn][17];
LL a[maxn],lb[maxn][17][66];
int log_2[maxn],deep[maxn],head[maxn];
int n,num,Q;
inline void add(int x,int y) {e[++num].v=y;e[num].nxt=head[x];head[x]=num;}
void dfs(int x) {
    for(re int i=head[x];i;i=e[i].nxt) 
    if(!deep[e[i].v]) {
        deep[e[i].v]=deep[x]+1;f[e[i].v][0]=x;
        dfs(e[i].v);
    }
}
inline void ins(LL x,int t,int b) {
    for(re int j=61;j>=0;--j)
    if(x>>j&1ll) {
        if(!lb[t][b][j]) {lb[t][b][j]=x;return;}
        x^=lb[t][b][j];
    }
}
inline void merge(int x,int y,int c,int d) {
    for(re int t=61;t>=0;--t) {
        if(!lb[c][d][t]) continue;
        ins(lb[c][d][t],x,y);
    }
}
inline void solve(int x,int y) {
    memset(lb[0][0],0,sizeof(lb[0][0]));
    if(x==y) {ins(a[x],0,0);return;}
    if(deep[x]<deep[y]) std::swap(x,y);
    for(re int i=log_2[deep[x]];i>=0;--i) 
    if(f[x][i]&&deep[f[x][i]]>=deep[y]) 
        merge(0,0,x,i),x=f[x][i];
    if(x==y) return;
    for(re int i=log_2[deep[x]];i>=0;--i) 
    if(f[x][i]!=f[y][i]) {
        merge(0,0,x,i),merge(0,0,y,i);
        x=f[x][i],y=f[y][i];
    }
    ins(a[x],0,0),ins(a[y],0,0);if(f[x][0])ins(a[f[x][0]],0,0);
}
int main()
{
    n=read(),Q=read();
    for(re int i=1;i<=n;i++) a[i]=read();
    int x,y;
    for(re int i=1;i<n;i++) x=read(),y=read(),add(x,y),add(y,x);
    deep[1]=1;dfs(1);
    for(re int i=2;i<=n;i++) log_2[i]=log_2[i>>1]+1;
    for(re int i=1;i<=n;i++) ins(a[i],i,0);
    for(re int i=1;i<=n;i++) if(f[i][0]) ins(a[f[i][0]],i,0);
    for(re int j=1;j<=log_2[n];j++)
        for(re int i=1;i<=n;i++) {
            if(!f[i][j-1]) continue;
            f[i][j]=f[f[i][j-1]][j-1];
            merge(i,j,i,j-1);merge(i,j,f[i][j-1],j-1);
        }
    while(Q--) {
        x=read(),y=read();
        solve(x,y);
        LL ans=0;
        for(re int i=61;i>=0;--i) if((ans^lb[0][0][i])>ans) ans^=lb[0][0][i];
        printf("%lld\n",ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/asuldb/p/10389288.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值