BZOJ4811 [Ynoi2017]由乃的OJ 树链剖分
原文链接http://www.cnblogs.com/zhouzhendong/p/8085286.html
题目传送门 - BZOJ4811
题意概括
是BZOJ3668长在树上并加上修改和区间询问。
一棵树,n个节点,每一个节点有一个位运算符和一个运算数。
现在要你支持两种操作:
1. 单点修改。
2. 现在你有一个数字v,让他从x走到y,每到达一个节点进行相应的运算。v在0~z之间,让你使得运算结果最大,问v为何值。
题解
我们考虑树链剖分+线段树。
假设某一位为0或者1,那么经过一定的操作之后也是0或1.
那么,如果只有一位,那么两段就可以轻松合并了。
k位也是一样,我们只需要用一堆奇怪的位运算就可以了,详见代码。
要维护正的和反的。然后好像没什么要说的了。
代码
#include <cstring> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cmath> using namespace std; typedef unsigned long long ULL; const int N=100005; struct Gragh{ int cnt,y[N*2],nxt[N*2],fst[N]; void clear(){ cnt=0; memset(fst,0,sizeof fst); } void add(int a,int b){ y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt; } }g; int n,m,k,op[N]; int fa[N],son[N],size[N],depth[N],top[N],p[N],ap[N],cnp=0; ULL Mod,val[N],n0=0,n1=~0; void Get_Gen_Info(int rt,int pre,int d){ size[rt]=1,son[rt]=-1,fa[rt]=pre,depth[rt]=d; for (int i=g.fst[rt];i;i=g.nxt[i]) if (g.y[i]!=pre){ int s=g.y[i]; Get_Gen_Info(s,rt,d+1); size[rt]+=size[s]; if (son[rt]==-1||size[s]>size[son[rt]]) son[rt]=s; } } void Get_Top(int rt,int tp){ top[rt]=tp; ap[p[rt]=++cnp]=rt; if (!~son[rt]) return; Get_Top(son[rt],tp); for (int i=g.fst[rt];i;i=g.nxt[i]){ int s=g.y[i]; if (s!=fa[rt]&&s!=son[rt]) Get_Top(s,s); } } struct STree{ ULL L0,R0,L1,R1; STree (){} STree (int x){L0=R0=0,L1=R1=-1;} STree (ULL a,ULL b,ULL c,ULL d){L0=a,R0=b,L1=c,R1=d;} void rev(){swap(L0,R0),swap(L1,R1);} void suit(){L0&=(Mod-1),L1&=(Mod-1),R0&=(Mod-1),R1&=(Mod-1);} }t[N*4]; STree operator + (STree a,STree b){ STree ans; ans.L0=((~a.L0)&b.L0)|(a.L0&b.L1); ans.L1=((~a.L1)&b.L0)|(a.L1&b.L1); ans.R0=((~b.R0)&a.R0)|(b.R0&a.R1); ans.R1=((~b.R1)&a.R0)|(b.R1&a.R1); return ans; } void build(int rt,int L,int R){ if (L==R){ int o=op[ap[L]]; ULL v=val[ap[L]]; if (o==1)t[rt].L0=t[rt].R0=n0&v,t[rt].L1=t[rt].R1=n1&v; if (o==2)t[rt].L0=t[rt].R0=n0|v,t[rt].L1=t[rt].R1=n1|v; if (o==3)t[rt].L0=t[rt].R0=n0^v,t[rt].L1=t[rt].R1=n1^v; return; } int mid=(L+R)>>1,ls=rt<<1,rs=ls|1; build(ls,L,mid); build(rs,mid+1,R); t[rt]=t[ls]+t[rs]; } void change(int rt,int L,int R,int pos,int o,ULL v){ if (L==R){ if (o==1)t[rt].L0=t[rt].R0=n0&v,t[rt].L1=t[rt].R1=n1&v; if (o==2)t[rt].L0=t[rt].R0=n0|v,t[rt].L1=t[rt].R1=n1|v; if (o==3)t[rt].L0=t[rt].R0=n0^v,t[rt].L1=t[rt].R1=n1^v; return; } int mid=(L+R)>>1,ls=rt<<1,rs=ls|1; if (pos<=mid) change(ls,L,mid,pos,o,v); else change(rs,mid+1,R,pos,o,v); t[rt]=t[ls]+t[rs]; } STree query(int rt,int L,int R,int xL,int xR){ if (R<xL||L>xR) return STree(0); if (xL<=L&&R<=xR) return t[rt]; int mid=(L+R)>>1,ls=rt<<1,rs=ls|1; return query(ls,L,mid,xL,xR)+query(rs,mid+1,R,xL,xR); } ULL Tquery(int a,int b,ULL z){ int f1=top[a],f2=top[b],rev=0; STree ansa(0),ansb(0); while (f1!=f2){ if (depth[f1]<depth[f2]) swap(f1,f2),swap(a,b),swap(ansa,ansb),rev^=1; ansa=query(1,1,n,p[f1],p[a])+ansa; a=fa[f1],f1=top[a]; } if (depth[a]>depth[b]) swap(a,b),swap(ansa,ansb),rev^=1; ansa.rev(); STree ans=ansa+query(1,1,n,p[a],p[b])+ansb; if (rev) ans.rev(); ULL ansv=0; // ans.suit(); for (int i=k;~i;i--){ if (ans.L0&(1ULL<<i)) ansv|=1ULL<<i; else if (z>=(1ULL<<i)&&(ans.L1&(1ULL<<i))) ansv|=1ULL<<i,z-=1ULL<<i; } return ansv; } int main(){ g.clear(); scanf("%d%d%d",&n,&m,&k); for (int i=1;i<=n;i++) scanf("%d%llu",&op[i],&val[i]); Mod=1ULL<<k; for (int i=1,a,b;i<n;i++){ scanf("%d%d",&a,&b); g.add(a,b),g.add(b,a); } Get_Gen_Info(1,0,0); Get_Top(1,1); build(1,1,n); for (int i=1;i<=m;i++){ int op,x,y; ULL z; scanf("%d%d%d%llu",&op,&x,&y,&z); if (op==1) printf("%llu\n",Tquery(x,y,z)); else change(1,1,n,p[x],y,z); } return 0; }