幸运数字 线性基+lca

题目链接:https://vjudge.net/problem/LibreOJ-2013

题解:

不会线性基的同学可以到这篇博客里学习一下:https://blog.csdn.net/a_forever_dream/article/details/83654397#comments (这篇博客应该是写的最详细的了,特别是我提醒了博主删除操作有错误后

做法有很多,用线性基结合倍增、点分治、树剖等都可以做出这道题,这里仅给出最基本最简单的倍增做法。

预先算出包含每个点往上2k个元素的线性基,求解答案的时候从两个点按照倍增求lca的方式向上跳,过程中不断将 包含上跳距离个点的线性基 合并,就可以得到最终整条路径上值的线性基,再根据线性基判断出整条路径的异或最大值。(合并两个线性基其实就是把一个线性基插入到另一个线性基)

每次询问上跳logn次,单次合并是log2n的,因此单次询问复杂度为log3n,已经足够通过此题。

倍增做法还可以通过RMQ的思想再优化掉一个log(不过我下面给出的代码没写),假设要询问a到b的答案,只要找到不超过a到lca(a,b)距离的最高2的幂次k,然后O(logn)找到上跳2k长度后到达lca(a,b)的点p,将a的2k线性基和p的2k线性基合并就可以得到a到lca(a,b)路径的线性基,b到lca(a,b)路径同理。这样单次询问的合并次数就减为了3次,单次询问的复杂度就只等于合并线性基的O(log2n)。

线性基结合点分治好像也可以做到O(log2n)的优秀复杂度,大家可以自行探索(主要是我不会。总的来说算是一道可以训练线性基和其它数据结构结合的比较好的题目。

代码:

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define maxn 20100
using namespace std;
typedef long long ll;

const ll mod=998244353;
const long double eps=1e-5;

int n, q, lg[maxn], fa[maxn][20], deep[maxn];
ll lb[maxn][20][61], buf1[61], buf2[61], num[maxn];
vector<int> edge[maxn];

void inse(ll x, ll *res)
{
    for (ll i=60; i>=0; i--)
    {
        if (!(x&(1ll<<i)))
            continue;
        if (!res[i])
        {
            res[i]=x;
            break;
        }
        x^=res[i];
    }
}

void merg(ll *a1, ll *a2, ll *res)
{
    for (ll i=0; i<=60; i++)
        res[i]=a1[i];
    for (ll i=60; i>=0; i--)
    {
        ll temp=a2[i];
        for (ll j=60; j>=0; j--)
        {
            if (!(temp&(1ll<<j)))
                continue;
            if (!res[j])
            {
                res[j]=temp;
                break;
            }
            temp^=res[j];
        }
    }
}

void dfs(ll now, ll f)
{
    deep[now]=deep[f]+1;
    fa[now][0]=f;
    inse(num[now], lb[now][0]);
    inse(num[f], lb[now][0]);
    for (ll i=1; (1ll<<i)<=deep[now]; i++)
    {
        fa[now][i]=fa[fa[now][i-1]][i-1];
        merg(lb[now][i-1], lb[fa[now][i-1]][i-1], lb[now][i]);

    }
    for (auto i: edge[now])
        if (i!=f)
            dfs(i, now);
}

ll lca(ll x, ll y)
{
    memset(buf1, 0, sizeof(buf1));
    memset(buf2, 0, sizeof(buf2));
    if (deep[x]<deep[y])
        swap(x, y);
    while (deep[x]!=deep[y])
    {
        merg(buf1, lb[x][lg[deep[x]-deep[y]]-1], buf1);
        x=fa[x][lg[deep[x]-deep[y]]-1];
    }
    if (x==y) return x;
    for (ll i=lg[deep[x]]; i>=0; i--)
        if (fa[x][i]!=fa[y][i])
        {
            merg(buf1, lb[x][i], buf1);
            merg(buf2, lb[y][i], buf2);
            x=fa[x][i], y=fa[y][i];
        }
    merg(buf1, buf2, buf1);
    inse(num[fa[x][0]], buf1);
    inse(num[x], buf1);
    inse(num[y], buf1);
    return fa[x][0];
}

int main()
{

    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>n>>q;
    for (ll i=1; i<=n; i++)
        cin>>num[i];
    for (ll i=1; i<=n; i++)
        lg[i]=lg[i-1]+(i==(1ll<<lg[i-1]));
    for (ll i=1, u, v; i<n; i++)
    {
        cin>>u>>v;
        edge[u].push_back(v);
        edge[v].push_back(u);
    }
    dfs(1, 0);
    for (ll i=1, u, v; i<=q; i++)
    {
        cin>>u>>v;
        if (u==v)
        {
            cout<<num[u]<<"\n";
            continue;
        }
        lca(u, v);
        ll temp=0;
        for (ll j=60; j>=0; j--)

            if (!(temp&(1ll<<j)))
                temp^=buf1[j];
        cout<<temp<<"\n";
    }
}

猜你喜欢

转载自www.cnblogs.com/opppppppp/p/12814741.html