bzoj 4568 幸运数字【线性基】

题目大意:给定一棵树,求路径的最大异或和。

倍增 + 线性基
线性基暴力合并…
没什么说的…
怪我太弱了,省选的时候还不会,然后gg,现在才学…orz

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<cstring>
#include<string>
#define N 20005
#define LL long long
using namespace std;

int n,q,siz,x,y;LL w;
int first[N],next[N*2],to[N*2];
int d[N],g[N][16],p[N];
struct node
{
    LL ins[61];
    void clear()
    {
        memset(ins,0,sizeof(ins));
    }
    LL cal()
    {
        for (int i = 0;i <= 60;i ++) if (ins[i])
            for (int j = i+1;j <= 60;j ++)
                if ((ins[j] >> i) & 1) ins[j] ^= ins[i];
        LL ret = 0;
        for (int i = 60;~i;i --)
            ret = max(ret,ret ^ ins[i]);
        return ret;
    }

}A[N][16],ans;

void inser(int x,int y)
{
    next[++ siz] = first[x];
    first[x] = siz;
    to[siz] = y;
}

void un(node &a,node b)
{
    for (int i = 0;i <= 60;i ++) if (b.ins[i])
    {
        LL t = b.ins[i];
        for (int j = i;~j;j --)
            if ((t >> j) & 1)
            {
                if (!a.ins[j]) {a.ins[j] = t;break;}
                else t ^= a.ins[j];
            }
    }
}

void bfs()
{
    int head = 0,tail = 1;
    d[p[1] = 1] = 1;
    while (head ^ tail)
    {
        int x = p[++ head];
        for (int y,i = first[x];i;i = next[i])
            if (!d[y = to[i]])
            {
                d[y] = d[x] + 1,g[y][0] = x;
                for (int k = 0;g[y][k];k ++)
                {
                    g[y][k + 1] = g[g[y][k]][k];
                    A[y][k + 1] = A[y][k],un(A[y][k + 1],A[g[y][k]][k]);
                }
                p[++ tail] = y;
            }
    }
}

void jump(int x,int y)
{
    if (d[x] < d[y]) swap(x,y);
    for (int i = 14;~i;i --)
        if (d[g[x][i]] >= d[y])
            un(ans,A[x][i]),x = g[x][i];
    if (x == y) {un(ans,A[x][0]);return;}
    for (int i = 14;~i;i --)
        if (g[x][i] ^ g[y][i])
        {
            un(ans,A[x][i]),un(ans,A[y][i]);
            x = g[x][i],y = g[y][i];
        }
    un(ans,A[x][1]),un(ans,A[y][0]);
}

int main()
{
    scanf("%d%d",&n,&q);
    for (int i = 1;i <= n;i ++)
    {
        scanf("%lld",&w);
        for (int j = 60;~j;j --)
            if ((w >> j) & 1) {A[i][0].ins[j] = w;break;}
    }
    for (int i = 1;i < n;i ++)
    {
        scanf("%d%d",&x,&y);
        inser(x,y),inser(y,x);
    }
    bfs();
    while (q --)
    {
        scanf("%d%d",&x,&y);
        ans.clear();
        jump(x,y);
        printf("%lld\n",ans.cal());
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值