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

Posted DaD3zZ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ-4568幸运数字 树链剖分 + 线性基合并相关的知识,希望对你有一定的参考价值。

4568: [Scoi2016]幸运数字

Time Limit: 60 Sec  Memory Limit: 256 MB
Submit: 238  Solved: 113
[Submit][Status][Discuss]

Description

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

Input

第一行包含 2 个正整数 n ,q,分别表示城市的数量和旅行者数量。第二行包含 n 个非负整数,其中第 i 个整数 Gi 表示 i 号城市的幸运值。随后 n-1 行,每行包含两个正整数 x ,y,表示 x 号城市和 y 号城市之间有一条道路相连。随后 q 行,每行包含两个正整数 x ,y,表示这名旅行者的旅行计划是从 x 号城市到 y 号城市。N<=20000,Q<=200000,Gi<=2^60

Output

 输出需要包含 q 行,每行包含 1 个非负整数,表示这名旅行者可以保留的最大幸运值。

Sample Input

4 2
11 5 7 9
1 2
1 3
1 4
2 3
1 4

Sample Output

14
11

HINT

Source

Solution

树上线性基

感觉可以有多重做法(1)树链剖分+线性基合并(2)LCA+倍增+线性基合并(3)点分治+线性基合并

后两个是$logn$级的,第一种是$log^{2}n$级的,自己一开始算错了复杂度,于是开心的写了(1),不过BZOJ开到60s时限,所以还是可以水过的

大体上就是树链剖分一下,线段树维护区间线性基,两个叶节点的线性基合并起来就是根的线性基,具体的合并,就是直接暴力把一个插入到另一个里面

然后查询的时候正常的查询就好了.

LCA+倍增的做法就是倍增预处理出$2^{j}$步的线性基,预处理和查询的时候,同样都是暴力合并线性基

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstring>
using namespace std;
long long read()
{
    long long 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*10+ch-0; ch=getchar();}
    return x*f; 
}
#define maxn 20010
int n,q; long long val[maxn];
struct EdgeNode{int next,to;}edge[maxn<<1];
int head[maxn],cnt;
void add(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
void insert(int u,int v) {add(u,v); add(v,u);}
//==============================================================================================
int size[maxn],deep[maxn],fa[maxn],son[maxn],pl[maxn],sz,pr[maxn],top[maxn],father[maxn][17];long long pre[maxn];
void dfs_1(int now)
{
    size[now]=1;
    for (int i=1; i<=16; i++)
        if (deep[now]>=(1<<i))
            father[now][i]=father[father[now][i-1]][i-1];
        else break;
    for (int i=head[now]; i; i=edge[i].next)
        if (edge[i].to!=fa[now])
            {
                deep[edge[i].to]=deep[now]+1;
                fa[edge[i].to]=now; father[edge[i].to][0]=now;
                dfs_1(edge[i].to);
                if (size[edge[i].to]>size[son[now]]) son[now]=edge[i].to;
                size[now]+=size[edge[i].to];
            }
}
void dfs_2(int now,int chain)
{
    pl[now]=++sz; pre[sz]=val[now]; top[now]=chain;
    if (son[now]) dfs_2(son[now],chain);
    for (int i=head[now]; i; i=edge[i].next)
        if (edge[i].to!=fa[now] && edge[i].to!=son[now])
            dfs_2(edge[i].to,edge[i].to);
    pr[now]=sz;
}
int LCA(int x,int y)
{
    if (deep[x]<deep[y]) swap(x,y);
    int dd=deep[x]-deep[y];
    for (int i=0; i<=16; i++)
        if (dd&(1<<i)) x=father[x][i];
    for (int i=16; i>=0; i--)
         if (father[x][i]!=father[y][i])
             x=father[x][i],y=father[y][i];
    if (x==y) return x; else return father[x][0];
}
//==============================================================================================
struct XXJ{long long a[61];}Ans;
struct TreeNode{int l,r;XXJ X;}tree[maxn<<2];
void Merge(XXJ &x,XXJ &y,XXJ &z)
{
    XXJ tmp;
    for (int i=60; i>=0; i--) tmp.a[i]=y.a[i];
    for (int i=60; i>=0; i--) 
        if (z.a[i])
            {
                long long tp=z.a[i];
                for (int i=60; i>=0; i--)
                    if (tp&(1LL<<i))
                        if (!tmp.a[i]) {tmp.a[i]=tp; break;}
                            else tp^=tmp.a[i];
            }
    for (int i=60; i>=0; i--)  x.a[i]=tmp.a[i];
}
void BuildTree(int now,int l,int r)
{
    tree[now].l=l; tree[now].r=r;
    if (l==r) 
        {
            memset(tree[now].X.a,0LL,sizeof(tree[now].X.a));
            long long tp=pre[l];
                for (int i=60; i>=0; i--)
                    if (tp&(1LL<<i))
                        if (!tree[now].X.a[i]) {tree[now].X.a[i]=tp; break;}
                            else tp^=tree[now].X.a[i];
            return;
        }
    int mid=(l+r)>>1;
    BuildTree(now<<1,l,mid); BuildTree(now<<1|1,mid+1,r);
    Merge(tree[now].X,tree[now<<1].X,tree[now<<1|1].X);
}
void Query(int now,int L,int R)
{
    if (L<=tree[now].l && R>=tree[now].r) {Merge(Ans,Ans,tree[now].X); return;}
    int mid=(tree[now].l+tree[now].r)>>1;
    if (L<=mid) Query(now<<1,L,R);
    if (R>mid) Query(now<<1|1,L,R);
}
//==============================================================================================
void Solve_Query(int x,int y)
{
    int z=LCA(x,y);
    if (deep[x]<deep[y]) swap(x,y);
    while (deep[top[x]]>deep[z])
        Query(1,pl[top[x]],pl[x]),x=fa[top[x]];
    Query(1,pl[z],pl[x]);
    if (y!=z)
        {
            int dd=deep[y]-deep[z]-1; z=y;    
            for (int i=0; i<=16; i++)
                if (dd&(1<<i)) z=father[z][i];
            if (dd==0) z=y; 
            while (deep[top[y]]>deep[z])
                Query(1,pl[top[y]],pl[y]),y=fa[top[y]];
            Query(1,pl[z],pl[y]);
        }
}
//==============================================================================================
int main()
{
    n=read();q=read();
    for (int i=1; i<=n; i++) val[i]=read();
    for (int u,v,i=1; i<=n-1; i++) u=read(),v=read(),insert(u,v);
    dfs_1(1); dfs_2(1,1); BuildTree(1,1,n);
    while (q--)
        {
            int x=read(),y=read(); long long ans=0;
            memset(Ans.a,0LL,sizeof(Ans.a));
            Solve_Query(x,y);
            for (int i=60; i>=0; i--) if ((ans^Ans.a[i])>ans) ans^=Ans.a[i];
            printf("%lld\n",ans);
        }
    return 0;
}

谢谢YJQ大大的数据,考前A题的快感

以上是关于BZOJ-4568幸运数字 树链剖分 + 线性基合并的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 4568: [Scoi2016]幸运数字树链剖分+线段树+线性基

LibreOJ #2013. 「SCOI2016」幸运数字

bzoj 4568: [Scoi2016]幸运数字

BZOJ4568: [Scoi2016]幸运数字

BZOJ 4568 幸运数字

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