睡觉困难综合征 树链剖分

睡觉困难综合征

LG传送门

树剖好题。

做这道题之前,请先做一做[NOI2014]起床困难综合症。记得我在我的树链剖分总结中的第一句话吗?这道题就是那道的上树带修版本。

首先简单口胡一下起床困难综合症:只需要把每一位初始状态为零和为一的结果预处理出来,这个过程可以通过把\(0\)\(inf\)进行一遍题目中要求的操作来得到;然后从高位往低位贪心即可。

上树带修之后就考虑用线段树维护上面预处理的结果。线段树维护四个值:

​ 从左往右\(0\)通过的结果:\(f[0][0]\)

​ 从左往右\(inf\)通过的结果:\(f[0][1]\)

​ 从右往左\(0\)通过的结果:\(f[1][0]\)

​ 从右往左\(inf\)通过的结果:\(f[1][1]\)

当两个区间合并时,设左区间为\(p\),右区间为\(q\),合并后的区间为\(k\),以\(k[0][0]\)的合并为例:若要使\(k[0][0]\)的某一位为\(1\),要么\(p[0][0]\)\(q[0][1]\)同时为\(1\),要么\(p[0][0]\)\(0\)\(q[0][1]\)\(1\),那么\(k[0][0]\)就是\((p[0][0]\&q[0][1])|((\)$p[0][0])&q[0][0])$。这个结论请自己手玩~证明,对于其他信息的合并也可以使用同样的方式推导。

在修改时,直接在线段树上更改某个点的值就好了。对于每一个询问,可以使用上面合并区间的方法合并重链上的信息,合并时注意谁是左区间谁右区间,从线段树上得到信息之后就按照区间上的做法一遍贪心得到答案。

注意必须使用unsigned long long。代码不长(2.9K),实现起来细节很多。

#include<cstdio>
#include<cctype>
#define R register
#define I inline
#define L unsigned long long
using namespace std;
const int S=100003,N=200003,M=400003;
const L inf=-1;
char buf[1000000],*p1,*p2;
I char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,S,stdin),p1==p2)?EOF:*p1++;}
I L rd(){
	L f=0; R char c=gc();
	while(c<48||c>57) c=gc();
	while(c>47&&c<58) f=f*10+(c^48),c=gc();
	return f;
}
int h[S],s[N],g[N],d[S],t[S],p[S],q[S],f[S],r[S],a[S],u[S],n,k,c;
L b[S],v[S];
struct E{
	L a[2][2];
	E(){a[0][0]=a[1][0]=0,a[0][1]=a[1][1]=inf;}
	I L* operator[](int x){return a[x];}
	I E operator+(E y){
		E x=*this,o;
		o[0][0]=(x[0][0]&y[0][1])|((~x[0][0])&y[0][0]);
		o[0][1]=(x[0][1]&y[0][1])|((~x[0][1])&y[0][0]);
		o[1][0]=(y[1][0]&x[1][1])|((~y[1][0])&x[1][0]);
		o[1][1]=(y[1][1]&x[1][1])|((~y[1][1])&x[1][0]);
		return o;
	}
}e[M];
I void swp(L&x,L&y){x^=y,y^=x,x^=y;}
I void add(int x,int y){s[++c]=h[x],h[x]=c,g[c]=y;}
void dfs1(int x,int f){
	p[x]=f,d[x]=d[f]+1,t[x]=1;
	for(R int i=h[x],y,m=0;i;i=s[i])
		if((y=g[i])^f){
			dfs1(y,x),t[x]+=t[y];
			if(t[y]>m)
				q[x]=y,m=t[y];
		}
}
void dfs2(int x,int t){
	f[x]=++c,r[x]=t,u[c]=a[x],v[c]=b[x];
	if(!q[x]) return;
	dfs2(q[x],t);
	for(R int i=h[x],y;i;i=s[i])
		if((y=g[i])^p[x]&&y^q[x])
			dfs2(y,y);
}
I void upd(int k,int x,L y){
	if(x==1)
		e[k][0][0]=e[k][1][0]=0,e[k][0][1]=e[k][1][1]=y;
	if(x==2)
		e[k][0][0]=e[k][1][0]=y,e[k][0][1]=e[k][1][1]=inf;
	if(x==3)
		e[k][0][0]=e[k][1][0]=y,e[k][0][1]=e[k][1][1]=inf^y;
}
I void psu(int k,int p,int q){e[k]=e[p]+e[q];}
void bld(int k,int l,int r){
	if(l==r){
		upd(k,u[l],v[l]);
		return ;
	}
	R int p=k<<1,q=p|1,m=l+r>>1;
	bld(p,l,m),bld(q,m+1,r),psu(k,p,q);
}
void mdf(int k,int l,int r,int x,int y,L z){
	if(l==r){
		upd(k,y,z);
		return ;
	}
	R int p=k<<1,q=p|1,m=l+r>>1;
	if(x<=m)
		mdf(p,l,m,x,y,z);
	else
		mdf(q,m+1,r,x,y,z);
	psu(k,p,q);
}
E qry(int k,int l,int r,int x,int y){
	if(x<=l&&r<=y)
		return e[k];
	R int p=k<<1,q=p|1,m=l+r>>1;
	E o;
	if(x<=m)
		o=o+qry(p,l,m,x,y);
	if(m<y)
		o=o+qry(q,m+1,r,x,y);
	return o;
}
L qry0(int x,int y,L z){
	E t,u,v;
	L o=0;
	while(r[x]^r[y])
		if(d[r[x]]>d[r[y]]){
			t=qry(1,1,n,f[r[x]],f[x]),swp(t[0][0],t[1][0]),swp(t[0][1],t[1][1]);
			u=u+t,x=p[r[x]];
		}
		else
			v=qry(1,1,n,f[r[y]],f[y])+v,y=p[r[y]];
	if(d[x]>d[y])
		t=qry(1,1,n,f[y],f[x]),swp(t[0][0],t[1][0]),swp(t[0][1],t[1][1]),u=u+t+v;
	else
		u=u+qry(1,1,n,f[x],f[y])+v;
	for(R int i=k-1;~i;--i)
		if(u[0][0]>>i&1)
			o+=1llu<<i;
		else
			if((u[0][1]>>i)&1&&1llu<<i<=z)
				o+=1llu<<i,z-=1llu<<i;
	return o;
}
int main(){
	R int m,i,o,x,y;
	L z;
	n=rd(),m=rd(),k=rd();
	for(i=1;i<=n;++i)
		a[i]=rd(),b[i]=rd();
	for(i=1;i<n;++i)
		x=rd(),y=rd(),add(x,y),add(y,x);
	c=0,dfs1(1,0),dfs2(1,1),bld(1,1,n);
	for(i=1;i<=m;++i){
		o=rd(),x=rd(),y=rd(),z=rd();
		if(o==1)
			printf("%llu\n",qry0(x,y,z));
		else
			mdf(1,1,n,f[x],y,z);
	}
	return 0;
}

posted @ 2018-12-21 12:35  newbiechd  阅读(290)  评论(0编辑  收藏  举报