1. 程式人生 > >[SCOI2016] 幸運數字

[SCOI2016] 幸運數字

Description

給定一棵樹,每個點有點權。每次詢問兩個點 \(x,y\),求 \(x\)\(y\) 的路徑上選擇若干個點的點權異或和最大值。\(n\leq 2\cdot 10^4,q\leq 2\cdot 10^5\)

Solution

這種樹上路徑的題一般就是樹剖啊點分治啊倍增啊。

這題顯然要維護線性基,線性基合併最低複雜度 \(\log^2n\)

如果樹剖或者倍增維護線性基都是 \(q\log^3 n\) 的,但是都可以線上。

但是如果換成點分治做就是 \(q\log^2 n\) 十分優秀。

每次求出重心到每個點的線性基,只統計過重心的路徑的答案即可。

如果一條路徑只經過了一個點特判掉即可。

Code

#include<bits/stdc++.h>
using std::min;
using std::max;
using std::swap;
using std::vector;
typedef double db;
typedef long long ll;
#define pb(A) push_back(A)
#define pii std::pair<int,int>
#define all(A) A.begin(),A.end()
#define mp(A,B) std::make_pair(A,B)
#define int long long
const int B=62;
const int N=20005;

vector< pii > v[N];
int head[N],mx[N],vis[N],ok[N],val[N];
int n,m,cnt,tot,root,id,MX,sze[N],ans[N*10];

struct Edge{
    int to,nxt;
}edge[N<<1];

void add(int x,int y){
    edge[++cnt].to=y;
    edge[cnt].nxt=head[x];
    head[x]=cnt;
}

struct xxj{
    int a[B];

    xxj(){memset(a,0,sizeof a);}

    void clear(){memset(a,0,sizeof a);}

    void ins(int x){
        for(int i=61;~i;i--){
            if(x>>i&1){
                if(!a[i]){
                    a[i]=x;
                    return;
                } else x^=a[i];
            }
        }
    }

}f[N];

xxj hb(xxj a,xxj b){
    xxj c=a;
    for(int i=61;~i;i--)
        if(b.a[i]) c.ins(b.a[i]);
    return c;
}

void getroot(int now,int fa=0){
    mx[now]=0;sze[now]=1;
    for(int i=head[now];i;i=edge[i].nxt){
        int to=edge[i].to;
        if(vis[to] or to==fa) continue;
        getroot(to,now);sze[now]+=sze[to];
        mx[now]=max(mx[now],sze[to]);
    }  mx[now]=max(mx[now],tot-sze[now]);
    if(mx[now]<MX) MX=mx[now],root=now;
}

int getint(){
    int X=0,w=0;char ch=getchar();
    while(!isdigit(ch))w|=ch=='-',ch=getchar();
    while( isdigit(ch))X=X*10+ch-48,ch=getchar();
    if(w) return -X;return X;
}

int query(xxj a,int now=0){
    for(int i=61;~i;i--)
        if((now^a.a[i])>now)  
            now^=a.a[i];
    return now;
}

void dfs(int now,int fa){
    f[now]=f[fa];f[now].ins(val[now]);
    for(auto x:v[now]){
        if(ok[x.first]==id){
            xxj c=hb(f[now],f[x.first]);viss[x.second]=1;
            ans[x.second]=max(ans[x.second],query(c));
        }
    }
    for(int i=head[now];i;i=edge[i].nxt){
        int to=edge[i].to;
        if(!vis[to] and to!=fa)
            dfs(to,now);
    }
}

void upd(int now,int fa){
    ok[now]=id;
    for(int i=head[now];i;i=edge[i].nxt){
        int to=edge[i].to;
        if(!vis[to] and to!=fa)
            upd(to,now);
    }
}

void solve(int now){
    vis[now]=1;id=now;ok[now]=id;
    f[now].clear();f[now].ins(val[now]);
    for(int i=head[now];i;i=edge[i].nxt){
        int to=edge[i].to;
        if(!vis[to])
            dfs(to,now),upd(to,now);
    }
    for(int i=head[now];i;i=edge[i].nxt){
        int to=edge[i].to;
        if(!vis[to]){
            tot=sze[to];MX=1e9;
            getroot(to,0);solve(root);
        }
    }
}

signed main(){
    n=getint(),m=getint();
    for(int i=1;i<=n;i++) val[i]=getint();
    for(int i=1;i<n;i++){
        int x=getint(),y=getint();
        add(x,y),add(y,x);
    }
    for(int i=1;i<=m;i++){
        int x=getint(),y=getint();
        if(x==y) ans[i]=val[x];
        else v[x].pb(mp(y,i)),v[y].pb(mp(x,i));
    }
    tot=n,MX=1e9;getroot(1,0);
    solve(root);
    for(int i=1;i<=m;i++) 
        printf("%lld\n",ans[i]);
    return 0;
}