bzoj4568 [Scoi2016]幸运数字(LCA+线性基)

题目链接

分析:
学长说这叫:树上线性基

有多重做法
(1)树链剖分+线性基合并(2)LCA+倍增+线性基合并(3)点分治+线性基合并

后两个是 logn l o g n 级的,第一种是 log2n l o g 2 n 级的

就代码复杂度来说,当然是选择第二种方法啦

每个结点维护向上跳 2i 2 i 步经过的数字的线性基
线性基的合并直接暴力即可
最后的统计答案就是普通的线性基求异或和最大

tip

我在倍增的时候没有在线性基中插入该点的权值
所以在询问的时候,需要单独加入两端点的权值:A.insert(val[x]); A.insert(val[y]);

LCA不要写错了,不然WA的太冤

#include<bits/stdc++.h> 
#define ll long long

using namespace std;

const int N=20005;
struct po{
    ll b[65];
    void clear() {
        memset(b,0,sizeof(b));
    }
    void insert(ll x) {
        for (int i=63;i>=0;i--)
            if (x>>i&1) {
                if (b[i]) x^=b[i];
                else {
                    b[i]=x;
                    break;
                }
        }
    }
    void Insert(po B) {
        for (int i=63;i>=0;i--) 
            if (B.b[i])
                insert(B.b[i]);
    }
};
po a[N][20];
struct node{
    int y,nxt;
};
node way[N<<1];
int st[N],tot=0,n,m,pre[N][20],deep[N],lg;
ll val[N];

void add(int u,int w) {
    tot++;way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;
    tot++;way[tot].y=u;way[tot].nxt=st[w];st[w]=tot;
}

void dfs(int now,int fa,int dep) {
    pre[now][0]=fa;
    deep[now]=dep;
    a[now][0].insert(val[fa]);
    for (int i=st[now];i;i=way[i].nxt)
        if (way[i].y!=fa)
            dfs(way[i].y,now,dep+1); 
}

void prepare() {
    dfs(1,0,1);

    lg=log(n)/log(2);
    for (int i=1;i<=lg;i++)
        for (int j=1;j<=n;j++) {
            pre[j][i]=pre[pre[j][i-1]][i-1];
            a[j][i]=a[j][i-1];
            a[j][i].Insert(a[pre[j][i-1]][i-1]);
        }
}

void solve(int x,int y) {
    po A; A.clear();
    A.insert(val[x]); A.insert(val[y]);

    if (deep[x]<deep[y]) swap(x,y);
    int d=deep[x]-deep[y];
    if (d) 
        for (int i=0;i<=lg&&d;i++,d>>=1) 
            if (d&1) {
                A.Insert(a[x][i]);
                x=pre[x][i];
            }
    if (x!=y) {
        for (int i=lg;i>=0;i--)
            if (pre[x][i]!=pre[y][i]) {
                A.Insert(a[x][i]);
                A.Insert(a[y][i]);
                x=pre[x][i];
                y=pre[y][i];
            }
        A.insert(val[pre[x][0]]);
    }

    ll ans=0;
    for (int i=63;i>=0;i--)
        if (A.b[i]&&((ans^A.b[i])>ans))
            ans^=A.b[i];
    printf("%lld\n",ans);
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) 
        scanf("%lld",&val[i]);
    for (int i=1;i<n;i++) {
        int u,w;
        scanf("%d%d",&u,&w);
        add(u,w);
    }
    prepare();
    for (int i=1;i<=m;i++) {
        int x,y;
        scanf("%d%d",&x,&y);
        solve(x,y);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值