[SCOI2016][JZOJ4632]幸运数字

题目大意

一棵n个点的树,每个点有点权Gi。

有q个询问,每次询问在点x到点y路径上,选择一些点,使得异或和最大,这个最大和为多少。

1≤n≤2×104,1≤q≤2×105,Gi∈[0,260)


题目分析

对于小数据我们可以想到一种高斯消元解异或方程组的解法。从高位到低位,先假设当前位有1,然后在方程组中进行消元,如果没有冲突就填,有就填0,然后更新方程组。

但是满分数据很大,这样做是不行的。这里我们就要用到一种叫做线性基的强大的东西。我们先来看看什么叫线性基:

啊不~不是这个(这么说我就是神犇了?哈哈哈哈哈~~~)。

线性基是什么呢?就是给定一个二进制集合S,然后我们要求出一个最小的集合s,这个集合内任意子集异或和能构成的数的集合,和原本二进制集合任意子集异或和能构成的数的集合是一样的。

说白了就是高斯消元解异或方程组最后剩下的矩阵。

换句话说利用线性基内的互相异或,就可以得出原集合互相异或的结果。如果二进制位数为d,那么线性基大小显然不超过d。

可以证明,线性基内不存在异或和为0的子集。

我们采用d个d位二进制数代表一个线性基。第i位如果有数,那数一定是线性基里面第d为1的最小的数。

那么我们查询最大异或和时可以直接从高位到低位贪心,如果异或上线性基第i位能使答案更大就异或,否则不异或。

线性基的合并就是和高斯消元差不多,对于第二个线性基的每一个待插入数,我们枚举第一个线性基里的位置d,如果该位置有数且当前待插入数d位为1,就把待插入数和该位异或,如果该位置没有数且待插入数d位为1,就将待插入数插到这里。

设二进制有D位,那么合并线性基的时间复杂度就是O(D2)。

回到这题,使用倍增维护树上区间的线性基,暴力合并即可。

注意到这里合并线性基的复杂度很大,如果合并多次是很难承受的。因此这里我们要使用类RMQ的查询思想,分成四个区间(重叠部分不会影响答案),然后暴力合并。

总时间复杂度O(nlog2nD2+q(log2n+D2))。


代码实现

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cmath>

using namespace std;

typedef long long LL;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch==‘-‘?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-‘0‘,ch=getchar();
    return x*f;
}

const int N=20050;
const int M=N<<1;
const int LGN=15;
const int D=60;

struct linear_basic
{
    LL num[D];
};

typedef linear_basic LB;

LB operator+(LB x,LB y)
{
    for (int i=D-1;i>=0;i--)
        if (y.num[i])
            for (int j=D-1;j>=0;j--)
            {
                if (!y.num[i]) break;
                if (y.num[i]>>j&1)
                    if (x.num[j]) y.num[i]^=x.num[j];
                    else
                    {
                        x.num[j]=y.num[i];
                        break;
                    }
            }
    return x;
}

int last[N],high[N];
int tov[M],next[M];
int n,lgn,tot,q;
int fa[N][LGN];
LB f[N][LGN];

void insert(int x,int y){tov[++tot]=y,next[tot]=last[x],last[x]=tot;}

void dfs(int x)
{
    for (int i=last[x],y;i;i=next[i])
        if ((y=tov[i])!=fa[x][0])
            fa[y][0]=x,high[y]=high[x]+1,dfs(y);
}

void pre()
{
    lgn=trunc(log(n)/log(2));
    for (int j=1;j<=lgn;j++)
        for (int i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1],f[i][j]=f[i][j-1]+f[fa[i][j-1]][j-1];
}

int lca(int x,int y)
{
    if (high[x]>high[y]) swap(x,y);
    for (int i=lgn;i>=0;i--)
        if (high[fa[y][i]]>=high[x]) y=fa[y][i];
    if (x==y) return x;
    for (int i=lgn;i>=0;i--)
        if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

int p(int x,int h)
{
    for (int i=lgn;i>=0;i--)
        if (high[fa[x][i]]>=h) x=fa[x][i];
    return x;
}

LL query(int x,int y)
{
    int z=lca(x,y);
    int lgh;
    lgh=trunc(log(high[x]-high[z]+1)/log(2));
    LB lb0=f[x][lgh]+f[p(x,high[z]+(1<<lgh)-1)][lgh];
    lgh=trunc(log(high[y]-high[z]+1)/log(2));
    lb0=lb0+f[y][lgh]+f[p(y,high[z]+(1<<lgh)-1)][lgh];
    LL ret=0;
    for (int i=D-1;i>=0;i--) if ((ret^lb0.num[i])>ret) ret^=lb0.num[i];
    return ret;
}

int main()
{
    freopen("lucky.in","r",stdin),freopen("lucky.out","w",stdout);
    n=read(),q=read();
    for (int i=1;i<=n;i++)
    {
        LL x;
        scanf("%lld",&x);
        for (int j=D-1;j>=0;j--)
            if (x>>j&1)
            {
                f[i][0].num[j]=x;
                break;
            }
    }
    for (int i=1,x,y;i<n;i++)
    {
        x=read(),y=read();
        insert(x,y),insert(y,x);
    }
    dfs(1),pre();
    for (int i=1,x,y;i<=q;i++)
    {
        x=read(),y=read();
        printf("%lld\n",query(x,y));
    }
    fclose(stdin),fclose(stdout);
    return 0;
}
时间: 2024-08-30 01:17:07

[SCOI2016][JZOJ4632]幸运数字的相关文章

LibreOJ #2013. 「SCOI2016」幸运数字

二次联通门 : LibreOJ #2013. 「SCOI2016」幸运数字 /* LibreOJ #2013. 「SCOI2016」幸运数字 树链剖分 + 线段树 + 线性基合并 没什么可说的 对原树进行树链剖分 然后建线段树 每个区间维护一段线性基 每次暴力把一段插入另一段中 最后线性基求最大即可 注意线性基求最大时一定是倒着枚举的 */ #include <cstdio> #include <iostream> const int BUF = 12312334; char Bu

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 号

【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

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

【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 号城市

【bzoj1853】 Scoi2010—幸运数字

http://www.lydsy.com/JudgeOnline/problem.php?id=1853 (题目链接) 今天考试考了容斥,结果空知道结论却不会写= = 题意:求区间中不含6,8两个数字及由6,8组成的数字的倍数的的数有几个 Solution  容斥原理.  先把所有的幸运数字都蒯到一个数组里,将两两之间可以整除的数只留下一个小的.  接下来如果暴力组合统计答案的话肯定会TLE,因为就算去掉了可以被整除的数以后还是有1000多个幸运数组.我们考虑dfs,x记录当前已经枚举到了第几个

2014.11.12模拟赛【美妙的数字】| vijos1904学姐的幸运数字

美妙的数字(number.c/.cpp/.pas) 题目描述 黄巨大认为非负整数是美妙的,并且它的数值越小就越美妙.当然0是最美妙的啦. 现在他得到一串非负整数,对于每个数都可以选择先对它做二进制非运算(模二意义下0.1互换,注意前导0也要交换),然后在任意相邻的两个数之间插入二进制与.二进制或,或者二进制异或.现在他想知道这样计算完产生的最美妙的数字是多少. 一共T组数据.对于每组数据,第一行一个n,表示这组数据中一串数有多少个.下面n个非负整数,表示这串数. 样例输入 2 2 3 6 3 1