【bzoj4811】由乃的OJ
Solution
这题可以用树剖+线段树做也可以用LCT做,不过大体思路是一样的
(接下来先讲的是树剖+线段树的做法,再提LCT的做法)
首先位运算有一个非常优秀的性质就是写成二进制之后每位的运算是独立的
所以我们可以巧妙利用这个性质来做这题
我们考虑维护在二进制中每一位的\(0\)和\(1\)在从\(x\)走到\(y\)之后会变成什么,这样在查询的时候枚举每一位然后贪心就好了,“从\(x\)走到\(y\)”这个可以用树剖搞定,现在的问题是线段树中如何合并两段区间的答案(记为\(info\),这是一个结构体,\(info.x_0\)表示带\(0\)进去的答案,\(info.x_1\)表示带\(1\)进去的答案)
先不考虑时间和空间问题,假设我们对于每一位都种了一棵线段树,某一位的线段树种每个节点的\(info\)记录的就是将这位的\(0\)和\(1\)带进去走完\(dfn\)序为\(l\)到\(r\)的这段之后得到的结果(\(0\)或者\(1\))
再具体一点,假设我们将所有的节点按照\(dfn\)序存在\(lis\)数组里面,那么对于第\(i\)位的线段树中的\([l,r]\)的节点,\(info.x_0\)记录的是\(0\)依次与\(lis[l]....lis[r]\)这些节点进行计算最后得到结果的第\(i\)位,\(info.x_1\)记录的是\(2^i-1\)(额就是二进制下\(i\)个1)依次与\(lis[l]...lis[r]\)这些节点进行计算最后得到的结果的第\(i\)位
然后因为这里位数最多是\(64\),我们完全可以用一个unsigned long long将所有的位压起来,只用一棵线段树处理,这样就时间和空间都不会有问题了
那么现在的问题就是如何用左儿子和右儿子的信息来更新当前节点(\(x\))的信息了
先把式子写出来:
两个式子的原理差不多,这里就拿\(x_0\)的式子来解释一下:\((info[lch].x_0\&info[rch].x_1)\)这一部分算的是\(0\)经过\(lch\)的区间之后变成了\(1\),然后再经过\(rch\)之后变成\(0\)或者\(1\),\(((\sim info[lch].x_0)\& info[rch].x_0)\)这部分是\(0\)经过\(lch\)之后还是\(0\),然后经过\(rch\)再变成对应的数字
再具体一点的话就是,对于第一部分,取完\(\&\)之后只有变成\(1\)的\(0\)那些位置被保存了下来,如果说这位在经过\(info[rch].x_1\)之后变成了\(1\)的话,\(\&\)完了还是\(1\),否则就是\(0\),也就是相当于变成了走完\(lch\)和\(rch\)之后的结果,第二部分就是把\(info[lch].x_0\)取反一下,这样\(\&\)之后保留下来的就是变成\(0\)的那些位置,然后后面的部分就和变成\(0\)一样了
然后线段树搞完了,那直接套个树剖,查询的时候枚举每一位,贪心一下就好了
但是!这里有一个小问题,我们注意到位运算是不满足交换律的,也就是说涉及到一个方向的问题,所以线段树中应该要维护\(dfn[l]....dfn[r]\)和\(dfn[r]....dfn[l]\)这两个方向的\(info\),查询的时候也要注意方向是往上走还是往下走,合并的话后者和前者的唯一区别就是把\(lch\)和\(rch\)的位置换了一下(仔细想想就明白了)
那所以这题就做完了
这个时候我们再来看看LCT怎么做
然后我们会发现LCT好做很多== 因为。。LCT的瓶颈在于怎么维护\(info\)。。。所以你直接splay维护两个方向的\(info\)然后把update函数改成上面的那样合并,其他都是常规操作==
但是我。。一开始走上了一条错误的道路写了线段树然后就不想改了==
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
#define ull unsigned long long
#define Pr pair<ull,ull>
#define mp make_pair
using namespace std;
const int N=100010,SEG=N*4,TOP=63;
struct xxx{
int y,nxt;
}a[N*2];
int h[N],op[N],sz[N],son[N],pre[N],lis[N],dfn[N],top[N],dep[N];
ull pw[TOP+1],val[N];
int n,m,dfn_t,tot,K;
ull ans,One;
ull calc(int which,ull v){
if (op[which]==1) return v&val[which];
else if (op[which]==2) return v|val[which];
return v^val[which];
}
struct Data{/*{{{*/
ull x0,x1;
Data(){}
Data(ull xx0,ull xx1){x0=xx0; x1=xx1;}
friend Data merge(Data L,Data R){
Data ret;
ret.x0=(L.x0&R.x1)|((~L.x0)&(R.x0));
ret.x1=(L.x1&R.x1)|((~L.x1)&(R.x0));
return ret;
}
}rec1[N],rec2[N];/*}}}*/
namespace Seg{/*{{{*/
int ch[SEG][2];
Data info[SEG][2];//0(down: ->),1(up: <-) info[x][dir]
int tot,n;
void pushup(int x){
info[x][0]=merge(info[ch[x][0]][0],info[ch[x][1]][0]);
info[x][1]=merge(info[ch[x][1]][1],info[ch[x][0]][1]);
}
void _build(int x,int l,int r){
info[x][0]=Data(0,0); info[x][1]=info[x][0];
if (l==r){
info[x][0]=Data(calc(lis[l],0),calc(lis[l],One));
info[x][1]=info[x][0];
return;
}
int mid=l+r>>1;
ch[x][0]=++tot; _build(ch[x][0],l,mid);
ch[x][1]=++tot; _build(ch[x][1],mid+1,r);
pushup(x);
}
void build(int _n){n=_n; tot=1; _build(1,1,n);}
void _update(int x,int d,int lx,int rx){
if (lx==rx){
info[x][0]=Data(calc(lis[lx],0),calc(lis[lx],One));
info[x][1]=info[x][0];
return;
}
int mid=lx+rx>>1;
if (d<=mid) _update(ch[x][0],d,lx,mid);
else _update(ch[x][1],d,mid+1,rx);
pushup(x);
}
void update(int d){_update(1,d,1,n);}
Data _query(int x,int l,int r,int lx,int rx,int dir){
if (l<=lx&&rx<=r) return info[x][dir];
int mid=lx+rx>>1;
Data tmp;
if (r<=mid) return _query(ch[x][0],l,r,lx,mid,dir);
else if (l>mid) return _query(ch[x][1],l,r,mid+1,rx,dir);
else{
tmp=_query(ch[x][0],l,mid,lx,mid,dir);
if (dir==0) tmp=merge(tmp,_query(ch[x][1],mid+1,r,mid+1,rx,dir));
else tmp=merge(_query(ch[x][1],mid+1,r,mid+1,rx,dir),tmp);
return tmp;
}
}
Data query(int l,int r,int dir){return _query(1,l,r,1,n,dir);}
}/*}}}*/
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;}
void dfs(int fa,int x,int d){
int u;
son[x]=0; sz[x]=1; dep[x]=d; pre[x]=fa;
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa) continue;
dfs(x,u,d+1);
sz[x]+=sz[u];
if (sz[u]>sz[son[x]]) son[x]=u;
}
}
void dfs1(int fa,int x){
int u;
dfn[x]=++dfn_t; lis[dfn_t]=x;
if (son[x]){
top[son[x]]=top[x];
dfs1(x,son[x]);
}
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa||u==son[x]) continue;
top[u]=u;
dfs1(x,u);
}
}
Data Solve(int x,int y){//0:down 1:up
int cnt1=0,cnt2=0;
Data ret=Data(0,One);
while (top[x]!=top[y]){
if (dep[top[x]]>dep[top[y]]){
rec1[++cnt1]=Seg::query(dfn[top[x]],dfn[x],1);
x=pre[top[x]];
}
else{
rec2[++cnt2]=Seg::query(dfn[top[y]],dfn[y],0);
y=pre[top[y]];
}
}
if (dep[x]<dep[y])
rec2[++cnt2]=Seg::query(dfn[x],dfn[y],0);
else
rec1[++cnt1]=Seg::query(dfn[y],dfn[x],1);
for (int i=1;i<=cnt1;++i) ret=merge(ret,rec1[i]);
for (int i=cnt2;i>=1;--i) ret=merge(ret,rec2[i]);
return ret;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x,y,Q;
Data tmp;
ull tmp0,tmp1,z;
scanf("%d%d%d",&n,&m,&K);
One=1;One=One<<63;One=One*2-1;
for (int i=0;i<=K;++i) pw[i]=(ull)1<<i;
for (int i=1;i<=n;++i) scanf("%d%llu",op+i,val+i);
memset(h,-1,sizeof(h));
tot=0;
for (int i=1;i<n;++i){
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
dfs(0,1,1);
top[1]=1;
dfs1(0,1);
Seg::build(n);
for (int i=1;i<=m;++i){
scanf("%d%d%d%llu",&Q,&x,&y,&z);
if (Q==1){
tmp=Solve(x,y);
ans=0;
for (int i=TOP;i>=0;--i){
tmp0=(tmp.x0>>i)&1;
tmp1=(tmp.x1>>i)&1;
if (tmp0>=tmp1||pw[i]>z)
ans|=(tmp0?pw[i]:0);
else
ans|=(tmp1?pw[i]:0),z-=pw[i];
}
printf("%llu\n",ans);
}
else{
op[x]=y; val[x]=z;
Seg::update(dfn[x]);
}
}
}