P5354 [Ynoi2017] 由乃的 OJ(树剖、位运算)

前言

当暴力思路与题解中的“暴力”不同时,继续想优化往往就渐行渐远了…
所以当没有头绪时,要勇于跳出原有的转化!

这种位运算类型的优化似乎始终不在我的寄存器中…需要加强!

解析

不难想到按位考虑的 O ( n k log ⁡ 2 n ) O(nk\log^2n) O(nklog2n) 的树剖做法(LCT可以单log)。
考虑这样的实现: f 0 / 1 f_{0/1} f0/1 表示某一位原来是 0/1 目前的值。
合并: f o p = f r , f l , o p f_{op}=f_{r,f_{l,op}} fop=fr,fl,op
可以写成如下形式: f o p = ( f l , o p & f r , 1 ) ∣ ( ( f l , o p ⊕ 1 ) & f r , 0 ) f_{op}=(f_{l,op}\&f_{r,1})|((f_{l,op}\oplus1)\&f_{r,0}) fop=(fl,op&fr,1)((fl,op1)&fr,0)
那么我们就可以把 64 个布尔压成一个 unsigned long long,从而进行快速合并。
这样就把复杂度的 k 拿掉了,时间复杂度 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n)

代码

1ull<<i 写成 1<<i WA了半天…

#include<bits/stdc++.h>
#include<string>
using namespace std;
#define ll long long
#define ull unsigned ll
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")

inline ll read() {
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}
const int N=1e5+100;
const int mod=998244353;

bool mem1;

int n,m,k;

int op[N];
ull w[N],o;
int dep[N],siz[N],hson[N],top[N],fa[N],dfn[N],pos[N],tim;
vector<int>e[N];
void dfs1(int x,int f){
  siz[x]=1;
  dep[x]=dep[f]+1;
  fa[x]=f;
  for(int to:e[x]){
    if(to==f) continue;
    dfs1(to,x);
    if(siz[to]>siz[hson[x]]) hson[x]=to;
    siz[x]+=siz[to];
  }
  return;
}
void dfs2(int x,int tp){
  top[x]=tp;
  dfn[++tim]=x;pos[x]=tim;
  if(hson[x]) dfs2(hson[x],tp);
  for(int to:e[x]){
    if(to==fa[x]||to==hson[x]) continue;
    dfs2(to,to);
  }
  return;
}

#define mid ((l+r)>>1)
#define ls (k<<1)
#define rs (k<<1|1)
struct node{
  ull w[2];
};
node operator + (const node &x,const node &y){
  node o;
  o.w[0]=(x.w[0]&y.w[1])|((~x.w[0])&y.w[0]);
  o.w[1]=(x.w[1]&y.w[1])|((~x.w[1])&y.w[0]);
  return o;
}
node tr1[N<<2],tr2[N<<2];
inline void pushup(int k){
  tr1[k]=tr1[ls]+tr1[rs];
  tr2[k]=tr2[rs]+tr2[ls];
}
void build(int k,int l,int r){
  if(l==r){
    int x=dfn[l];
    if(op[x]==1){
      tr1[k]=tr2[k]=(node){0,w[x]};
    }
    else if(op[x]==2){
      tr1[k]=tr2[k]=(node){w[x],o};
    }
    else if(op[x]==3){
      tr1[k]=tr2[k]=(node){w[x],o^w[x]};
    }
    //printf("k=%d (%d %d) %llu %llu\n",k,l,r,tr1[k].w[0],tr1[k].w[1]);
    return;
  }
  build(ls,l,mid);
  build(rs,mid+1,r);
  pushup(k);
  //printf("k=%d (%d %d) %llu %llu\n",k,l,r,tr1[k].w[0],tr1[k].w[1]);
}
void upd(int k,int l,int r,int p){
  if(l==r){
    int x=dfn[l];
    if(op[x]==1){
      tr1[k]=tr2[k]=(node){0,w[x]};
    }
    else if(op[x]==2){
      tr1[k]=tr2[k]=(node){w[x],o};
    }
    else if(op[x]==3){
      tr1[k]=tr2[k]=(node){w[x],o^w[x]};
    }
    return;
  }
  if(p<=mid) upd(ls,l,mid,p);
  else upd(rs,mid+1,r,p);
  pushup(k);
}
node ask(int k,int l,int r,int x,int y,int op){
  //if(k==1) printf("ask: (%d %d) op=%d\n",x,y,op);
  if(x<=l&&r<=y){
    if(op==1) return tr1[k];
    else return tr2[k];
  }
  if(y<=mid) return ask(ls,l,mid,x,y,op);
  else if(x>mid) return ask(rs,mid+1,r,x,y,op);
  else if(op==1) return ask(ls,l,mid,x,y,op)+ask(rs,mid+1,r,x,y,op);
  else return ask(rs,mid+1,r,x,y,op)+ask(ls,l,mid,x,y,op);
}
#undef mid
#undef ls
#undef rs

node query(int x,int y){
  node res1=(node){0,o},res2=(node){0,o};
  while(top[x]!=top[y]){
    if(dep[top[x]]>=dep[top[y]]){
      res1=res1+ask(1,1,n,pos[top[x]],pos[x],2);
      x=fa[top[x]];
    }
    else{
      res2=ask(1,1,n,pos[top[y]],pos[y],1)+res2;
      y=fa[top[y]];
    }
  }
  if(dep[x]>=dep[y]) res1=res1+ask(1,1,n,pos[y],pos[x],2);
  else res2=ask(1,1,n,pos[x],pos[y],1)+res2;
  return res1+res2;
}  

bool mem2;
signed main() {
#ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
#endif
  n=read();m=read();k=read();
  if(k==0) o=0;
  else{
    o=1;
    for(int i=0;i<k;i++) o=(o<<1)|1;
  }
  for(int i=1;i<=n;i++) scanf("%d%llu",&op[i],&w[i]);
  for(int i=1;i<n;i++){
    int x=read(),y=read();
    e[x].push_back(y);
    e[y].push_back(x);
  }
  dfs1(1,0);
  dfs2(1,1);
  build(1,1,n);
  for(int i=1;i<=m;i++){
    int oop,x,y;
    ull z;
    scanf("%d%d%d%llu",&oop,&x,&y,&z);
    if(oop==2){
      op[x]=y;w[x]=z;
      upd(1,1,n,pos[x]);
    }
    else{
      node o=query(x,y);
      //printf("%llu %llu\n",o.w[0],o.w[1]);
      ull res(0);
      for(int i=k-1;i>=0;i--){
	if(o.w[0]&(1<<i)) res+=(1<<i);
	else if((o.w[1]&(1<<i))&&z>=(1<<i)){
	  z-=(1<<i);
	  res+=(1<<i);
	}
      }
      printf("%llu\n",res);
    }
  }
  return 0;
}
/*
  3 1
  2 3 3 1
  1 1
*/

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值