[SCOI2016]幸运数字 线性基

题面

题面

题解

题面意思非常明确:求树上一条链的最大异或和。
我们用倍增的思想。
将这条链分成2部分:x ---> lca , lca ---> y
分别求出这2个部分的线性基,然后合并,再求最大异或和。
所以我们现在只需要考虑如何在树上求一条无需拐弯的链的最大线性基。
考虑倍增。
我们预处理出f[i][j]表示从点i开始向上走\(2^j\)构成的链的线性基。至于只有点\(i\)一个点的线性基,我们可以在运算的时候特判一下处理。
暴力预处理后,我们就可以最多 合并3次 + 求LCA的复杂度 求出任意询问的答案了!
如果我们还是跟普通倍增一样暴力向上跳的话,是单次询问\(logn * 60 * 60\),所有询问的总复杂度\(q * logn * 60 * 60\),差不多都到百亿级别了!不知道为什么很多人用这种方法都过了。
所以我们考虑跟RMQ类似的思想,因为在线性基中插入重复串不会有什么影响,因此对于树上任意一条不带拐弯的链,我们完全可以拆分成2段长度至少为一半的链,然后合并即可。
虽然可能会有重复的部分,但是不会有影响。
所以就可以做到单次复杂度\(60 * 60 * 3\)啦,忽略3这个常数复杂度大概是7亿左右,时限6s基本还是可以勉强接受的范围。(这里的60其实是对一个longlong大小的数取log,因为比较大,所以不能忽略)

#include<bits/stdc++.h>
using namespace std;
#define R register int
#define LL long long
#define AC 21000
#define ac 40100

int n, m;
int Head[AC], Next[ac], date[ac], tot;
int dep[AC], fa[AC][15];
LL s[AC], ans;

inline LL read()
{
    LL x = 0;char c = getchar();
    while(c > '9' || c < '0') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x;
}

inline void add(int f, int w)
{
    date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot;
    date[++ tot] = f, Next[tot] = Head[w], Head[w] = tot;
}

struct basis{
    LL f[61];

    inline void clear() {memset(f, 0, sizeof(f));}
    inline void ins(LL x)
    {
        LL maxn = 1LL << 60;
        for(R i = 60; ~i; i --, maxn >>= 1)
        {
            if(!(x & maxn)) continue;
            if(!f[i]) {f[i] = x; break;}
            else x ^= f[i];
        }
    }

    inline void merge(basis x){for(R i = 0; i <= 60; i ++) ins(x.f[i]);}
}f[AC][15], a, b;

void dfs(int x)
{
    f[x][0].ins(s[x]), f[x][0].ins(s[fa[x][0]]);//直接按常规方法处理,使用的时候特判掉
    for(R i = 1; i <= 14; i ++)
    {
        fa[x][i] = fa[fa[x][i - 1]][i - 1];
        f[x][i] = f[x][i - 1], f[x][i].merge(f[fa[x][i - 1]][i - 1]);
    }
    for(R i = Head[x]; i; i = Next[i])
    {
        int now = date[i];
        if(now == fa[x][0]) continue;
        fa[now][0] = x, dep[now] = dep[x] + 1, dfs(now);
    }
}

inline int LCA(int x, int y)
{
    if(dep[x] < dep[y]) swap(x, y);
    for(R i = 14; i >= 0; i --)
        if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
    for(R i = 14; i >= 0; i --)
        if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
    if(x == y) return x;
    else return fa[x][0];
}

inline int kth(int x, int k)
{
    int lim = dep[x] - k;
    for(R i = 14; i >= 0; i --)
        if(dep[fa[x][i]] >= lim) x = fa[x][i];
    return x;
}

void cal(int x, int y)
{
    int lca = LCA(x, y), len1 = dep[x] - dep[lca], len2 = dep[y] - dep[lca];
    a.clear(), b.clear();
    if(!len1) a.ins(s[x]);
    else
    {
        LL tmp = 1, cnt = 0;
        for(R i = 0; i <= 60; i ++, tmp <<= 1, ++ cnt) if((tmp << 1) > len1) break;
        a = f[x][cnt], a.merge(f[kth(x, len1 - tmp)][cnt]);
    }
    if(!len2) b.ins(s[y]);
    else
    {
        LL tmp = 1, cnt = 0;
        for(R i = 0; i <= 60; i ++, tmp <<= 1, ++ cnt) if((tmp << 1) > len2) break;
        b = f[y][cnt], b.merge(f[kth(y, len2 - tmp)][cnt]);
    }
    a.merge(b);
}

void pre()
{
    n = read(), m = read(), fa[1][0] = dep[1] = 1;
    for(R i = 1; i <= n; i ++) s[i] = read();
    for(R i = 1; i < n; i ++) add(read(), read());
}

void work()
{
    for(R i = 1; i <= m; i ++)
    {
        cal(read(), read()), ans = 0;
        for(R j = 60; ~j; j --)
            if((ans ^ a.f[j]) > ans) ans ^= a.f[j];
        printf("%lld\n", ans);
    }
}

int main()
{
//  freopen("in.in", "r", stdin);
    pre();
    dfs(1);//建倍增数组
    work();
//  fclose(stdin);
    return 0;
}

原文地址:https://www.cnblogs.com/ww3113306/p/10354353.html

时间: 2024-09-30 09:48:30

[SCOI2016]幸运数字 线性基的相关文章

bzoj4568: [Scoi2016]幸运数字 线性基 倍增

洛谷T了,mmp 还是bzoj时限良心,虽然三天两头爆炸 算是简单的题吧,构造出每个点logn个的线性基表 感觉线性基最nb的就是只有log个,所以可以做的很暴力 合并就是拆开再并 1 #include <bits/stdc++.h> 2 using namespace std; 3 struct Bas 4 { 5 long long a[61]; 6 Bas() 7 { 8 for(int i=0;i<=60;i++) 9 a[i]=0; 10 } 11 void add(long

【BZOJ 4568】 4568: [Scoi2016]幸运数字 (线性基+树链剖分+线段树)

4568: [Scoi2016]幸运数字 Description A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一.每座城市都有一个 幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征.一些旅行者希望游览 A 国.旅行者计划 乘飞机降落在 x 号城市,沿着 x 号城市到 y 号城市之间那条唯一的路径游览,最终从 y 城市起飞离开 A 国. 在经过每一座城市时,游览者就会有机会与这座城市的幸运数字拍照,从而将这份幸运保存到自己身上.然而,幸

[BZOJ4568][SCOI2016]幸运数字(倍增LCA,点分治+线性基)

4568: [Scoi2016]幸运数字 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 2131  Solved: 865[Submit][Status][Discuss] Description A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一.每座城市都有一个 幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征.一些旅行者希望游览 A 国.旅行者计划 乘飞机降落在 x 号城市,沿着 x

bzoj 4568: [Scoi2016]幸运数字

4568: [Scoi2016]幸运数字 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 848  Solved: 336[Submit][Status][Discuss] Description A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一.每座城市都有一个 幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征.一些旅行者希望游览 A 国.旅行者计划 乘飞机降落在 x 号城市,沿着 x 号

luoguP3292 [SCOI2016]幸运数字(线性基+树上倍增)

传送:https://www.luogu.org/problem/P3292 题意: $n$座城市,$n-1$条路,每个城市有一个价值$a_i$.$q$个询问,每次询问城市$x$到城市$y$的路径上经过的城市的价值的最大异或和为多少. 数据范围: $1<=n<=20000,q<=200000,a_i<=2^{60}$. 分析: 对于一次询问$x-->y$的路径上的答案,很明显就是$x-->lca(x,y)$的线性基并上$y-->lca(x,y)$的线性基,然后求最

bzoj4568 [Scoi2016]幸运数字

Description A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一.每座城市都有一个幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征.一些旅行者希望游览 A 国.旅行者计划乘飞机降落在 x 号城市,沿着 x 号城市到 y 号城市之间那条唯一的路径游览,最终从 y 城市起飞离开 A 国.在经过每一座城市时,游览者就会有机会与这座城市的幸运数字拍照,从而将这份幸运保存到自己身上.然而,幸运是不能简单叠加的,这一点游览者也十分清楚.他们迷

BZOJ4568:[SCOI2016]幸运数字——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=4568 https://www.luogu.org/problemnew/show/P3292 A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一.每座城市都有一个幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征. 一些旅行者希望游览 A 国.旅行者计划乘飞机降落在 x 号城市,沿着 x 号城市到 y 号城市之间那条唯一的路径游览,最终从 y

SCOI2016 幸运数字

传送门 如果只是一条路径的话,那就是非常简单的线性基. 不过要考虑多组询问-- 考虑到n比较小,我们可以模仿倍增LCA的方法,预处理倍增的线性基.在每次路径上跳的时候把线性基合并最后求解即可.具体的做法是,我们用\(p[i][x][j]\)表示在编号为x的点处,向上跳\(2^i\)步以内,线性基第j位的数.这个可以合并,比较好写的方法就是直接传两个数组进去合并. 之后就正常了.代码很短,不到100行.(我交了好多次因为忘记用longlong快读了--) #include<iostream> #

【BZOJ-4568】幸运数字 树链剖分 + 线性基合并

4568: [Scoi2016]幸运数字 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 238  Solved: 113[Submit][Status][Discuss] Description A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一.每座城市都有一个幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征.一些旅行者希望游览 A 国.旅行者计划乘飞机降落在 x 号城市,沿着 x 号城市