洛谷 P3292 【SCOI2016】幸运数字/BZOJ 4568 幸运数字 题解

洛谷 P3292 【SCOI2016】幸运数字/BZOJ 4568 幸运数字 题解

2020-2-12 xiaoh

题意

给一张含有nn个节点的树(1n2×1041\leq n \leq 2\times 10^4),第ii个节点上有11个数ai(1ai260)a_i(1\leq a_i \leq 2^{60})。接下来有qq个询问(1q2×1051\leq q \leq 2\times 10^5),每个询问给定两个点u,vu,v,求两点间路径上包含的点的子集的最大异或和。

题解

看到子集异或和,一下子想到线性基。又因为要维护树链上的信息,所以自然想到了树链剖分万能的LCA。于是乎我们考虑用LCA的倍增来维护线性基就可以通过啦!时间复杂度O(nw2logn+mw2)O(nw^2logn+mw^2),空间复杂度O(nwlogn)O(nwlogn),虽然复杂度一路狂飙直上10910^9,然鹅我们可以看一看数据:洛谷是2s2s~6s6s,BZOJ是60s60s,随便在线性基上加几个registerregister卡卡常就能轻松飘过去。

Code

#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void read(T &x)
{
    x=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
    return;
}
template<typename T>
inline void write(T x)
{
    if(x>=10) write(x/10);
    putchar(x%10+'0');
}
const int MAXN=20010,LOGN=15,W=61;
class LinearBasis{//封装后的线性基板子
public:
    long long a[W+1];
    inline void init()//初始化
    {
        memset(a,0,sizeof(a));
    }
    inline void insert(long long x)//插入
    {
        for(register int i=W;i>=0;i--)
        {
            if(!x) return;
            if(!((x>>i)&1ll)) continue;
            if(a[i]) x^=a[i];
            else
            {
                for(register int j=0;j<i;j++)
                if((x>>j)&1ll) x^=a[j];
                for(register int j=i+1;j<=W;j++)
                if((a[j]>>i)&1ll) a[j]^=x;
                a[i]=x;
                return;
            }
        }
    }
    inline void merge(LinearBasis x)//合并两个线性基
    {
        for(register int i=0;i<=W;i++) insert(x.a[i]);
    }
    inline long long query()//查询子集xor和
    {
        long long ret=0;
        for(register int i=0;i<=W;i++) ret^=a[i];
        return ret;
    }
}f[MAXN][LOGN+1];
int n,m;
long long a[MAXN];
int tot=1;
int edge[MAXN*2],nxt[MAXN*2],hd[MAXN];
inline void add_edge(int u,int v)//链式前向星
{
    edge[tot]=v,nxt[tot]=hd[u],hd[u]=tot++;
}
bool book[MAXN];
queue<int> q;
int LCA[MAXN][LOGN+1],d[MAXN];
inline void prework()//LCA预处理
{
    d[1]=1,book[1]=1,q.push(1);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(register int i=hd[x];i;i=nxt[i])
        {
            int y=edge[i];
            if(book[y]) continue;
            LCA[y][0]=x,f[y][0].insert(a[x]);
            d[y]=d[x]+1;
            book[y]=1,q.push(y);
            for(int j=1;j<=LOGN;j++)
            LCA[y][j]=LCA[LCA[y][j-1]][j-1],
            f[y][j].merge(f[y][j-1]),f[y][j].merge(f[LCA[y][j-1]][j-1]);
        }
    }
}
inline long long query(int x,int y)//倍增查询
{
    LinearBasis ans;
    ans.init();
    ans.insert(a[x]),ans.insert(a[y]);
    if(d[x]<d[y]) swap(x,y);
    for(int i=LOGN;i>=0;i--)
    if(d[LCA[x][i]]>=d[y]) ans.merge(f[x][i]),x=LCA[x][i];
    if(x==y) return ans.query();
    for(int i=LOGN;i>=0;i--)
    if(LCA[x][i]!=LCA[y][i])
    ans.merge(f[x][i]),ans.merge(f[y][i]),x=LCA[x][i],y=LCA[y][i];
    ans.merge(f[x][0]);
    return ans.query();
}
int main()
{
    read(n),read(m);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<n;i++)
    {
        int u,v;
        read(u),read(v);
        add_edge(u,v),add_edge(v,u);
    }
    prework();
    for(int i=1;i<=m;i++)
    {
        int u,v;
        read(u),read(v);
        write(query(u,v)),putchar('\n');
    }
}