[线性基 树链剖分 线段树 || ST表 || 点分治] BZOJ 4568 [Scoi2016]幸运数字

这个东西链剖之后就是个裸的区间线性基,是可以暴力合并的O(log2n)
所以暴力线段树是 O(nlog4n)
改成ST表是 O(nlog3n)
而如果点分的话 应该是O(nlog2n)

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline void read(ll &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=20005;
const int K=61;
const int KK=17;

ll tmp[N],pnt;

struct Base{
  int base[K]; ll b[K];
  Base(){ }
  Base(ll x){
    *b=0; b[++*b]=x; for (int k=K-1;~k;k--) if (x>>k&1) { base[k]=1; break; }
  }
  void clear(){
    cl(base); *b=0;
  }
  void Merge(ll *x,ll *y){
    pnt=0;
    for (int i=1;i<=*x;i++) tmp[++pnt]=x[i];
    for (int i=1;i<=*y;i++) tmp[++pnt]=y[i];
    cl(base); *b=0;
    for (int i=1;i<=pnt;i++){
      ll x=tmp[i];
      for (int k=K-1;~k;k--)
    if (x>>k&1)
      if (!base[k]){
        b[++*b]=x; base[k]=*b;
        //for (int j=1;j<*b;j++)
        //  if (b[j]>>k&1)
        //  b[j]^=x;
        break;
      }else
        x^=b[base[k]];
    }
  }
  ll Query(){
    ll x=0;
    for (int k=K-1;~k;k--)
      if (base[k] && (x^b[base[k]])>x)
    x^=b[base[k]];
    return x;
  }
}st[N][KK];

struct edge{
  int u,v,next;
}G[N<<1];
int head[N],inum;
inline void add(int u,int v,int p){
  G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}
#define V G[p].v

int n;
ll val[N];

int size[N],fat[N];
int depth[N];

inline void dfs(int u,int fa){
  size[u]=1; fat[u]=fa; depth[u]=depth[fa]+1;
  for (int p=head[u];p;p=G[p].next)
    if (V!=fa)
      dfs(V,u),size[u]+=size[V];
}

int pre[N],clk,top[N],back[N];
inline void find(int u,int fa,int z){
  pre[u]=++clk; top[u]=z; back[clk]=u;
  int maxv=0,son=0;
  for (int p=head[u];p;p=G[p].next)
    if (V!=fa && size[V]>maxv)
      maxv=size[son=V];
  if (son) find(son,u,z);
  for (int p=head[u];p;p=G[p].next)
    if (V!=fa && V!=son)
      find(V,u,V);
}

int Log[N];
inline void Build(){
  for (int i=2;i<=n;i++) Log[i]=Log[i>>1]+1;
  for (int i=1;i<=n;i++) st[i][0]=Base(val[back[i]]);
  for (int k=1;k<KK;k++)
    for (int i=1;i<=n;i++)
      st[i][k].Merge(st[i][k-1].b,st[min(i+(1<<(k-1)),n+1)][k-1].b);
}

Base Ret;

inline void merge(int l,int r){
  int t=Log[r-l+1];
  Ret.Merge(Ret.b,st[l][t].b);
  Ret.Merge(Ret.b,st[r-(1<<t)+1][t].b);
}

inline ll Query(int u,int v){
  Ret.clear();
  for (;top[u]!=top[v];u=fat[top[u]]){
    if (depth[top[u]]<depth[top[v]]) swap(u,v);
    merge(pre[top[u]],pre[u]);
  }
  if (depth[u]>depth[v]) swap(u,v);
  merge(pre[u],pre[v]);
  return Ret.Query();
}

int main(){
  int x,y,Q;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n); read(Q);
  for (int i=1;i<=n;i++) read(val[i]);
  for (int i=1;i<n;i++) read(x),read(y),add(x,y,++inum),add(y,x,++inum);
  dfs(1,0); find(1,0,1); Build();
  while (Q--){
    read(x); read(y);
    printf("%lld\n",Query(x,y));
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值