[BZOJ]4568: [Scoi2016]幸运数字 倍增+线性基

Description

A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一。每座城市都有一个幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征。一些旅行者希望游览 A 国。旅行者计划乘飞机降落在 x 号城市,沿着 x 号城市到 y 号城市之间那条唯一的路径游览,最终从 y 城市起飞离开 A 国。在经过每一座城市时,游览者就会有机会与这座城市的幸运数字拍照,从而将这份幸运保存到自己身上。然而,幸运是不能简单叠加的,这一点游览者也十分清楚。他们迷信着幸运数字是以异或的方式保留在自己身上的。例如,游览者拍了 3 张照片,幸运值分别是 5,7,11,那么最终保留在自己身上的幸运值就是 9(5 xor 7 xor 11)。有些聪明的游览者发现,只要选择性地进行拍照,便能获得更大的幸运值。例如在上述三个幸运值中,只选择 5 和 11 ,可以保留的幸运值为 14 。现在,一些游览者找到了聪明的你,希望你帮他们计算出在他们的行程安排中可以保留的最大幸运值是多少。

题解:

就倍增+线性基嘛, b[i][j] 表示 i i的第 2j 个祖先的线性基,然后搞定合并两个线性基这个操作,询问的时候合并一下路径上线性基,最后从高位到低位贪心,能使答案变化就选,就行了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=20010;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
struct Edge{int y,next;}e[Maxn*2];
int last[Maxn],len=0;
void ins(int x,int y){int t=++len;e[t].y=y;e[t].next=last[x];last[x]=t;}
int n,q,fa[Maxn][17],dep[Maxn];
LL G[Maxn],b[Maxn][17][62];
void insb(int x,int y,LL z)
{
    for(int i=61;i>=0;i--)
    {
        if((z>>i)&1)
        {
            if(!b[x][y][i]){b[x][y][i]=z;break;}
            else z^=b[x][y][i];
        }
    }
}
void merge(int x1,int y1,int x2,int y2)//b[x1][y1]并到b[x2][y2]中
{
    for(int i=61;i>=0;i--)
    if(b[x1][y1][i])insb(x2,y2,b[x1][y1][i]);
}
void dfs(int x,int f)
{
    fa[x][0]=f;dep[x]=dep[f]+1;
    for(int i=1;i<=15;i++)
    if((1<<i)<=dep[x])fa[x][i]=fa[fa[x][i-1]][i-1],merge(x,i-1,x,i),merge(fa[x][i-1],i-1,x,i);
    for(int i=last[x];i;i=e[i].next)
    {
        int y=e[i].y;
        if(y==fa[x][0])continue;
        dfs(y,x);
    }
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int i=15;i>=0;i--)if((1<<i)<=dep[x]-dep[y])x=fa[x][i];
    if(x==y)return x;
    for(int i=15;i>=0;i--)
    if((1<<i)<=dep[x]&&fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
void solve(int x,int y)
{
    int lca=LCA(x,y);memset(b[n+1][0],0,sizeof(b[n+1][0]));//存放答案的位置
    for(int i=15;i>=0;i--)if((1<<i)<=dep[x]-dep[lca])merge(x,i,n+1,0),x=fa[x][i];
    for(int i=15;i>=0;i--)if((1<<i)<=dep[y]-dep[lca])merge(y,i,n+1,0),y=fa[y][i];
    merge(lca,0,n+1,0);
    LL ans=0;
    for(int i=61;i>=0;i--)
    if((ans^b[n+1][0][i])>ans)ans^=b[n+1][0][i];
    printf("%lld\n",ans);
}
int main()
{
    memset(b,0,sizeof(b));
    n=read();q=read();
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&G[i]);
        insb(i,0,G[i]);
    }
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        ins(x,y);ins(y,x);
    }
    dep[0]=0;dfs(1,0);
    while(q--)
    {
        int x=read(),y=read();
        solve(x,y);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值