• BZOJ4811: [YNOI2017] 由乃的OJ


    题目大意

    给定一棵$N$个点的树$(Nleq 100000)$,每个节点有一个点权和一种位运算符号(&(与)、|(或)、^(异或)三种之一),支持两种操作:

    1、给定一个点$u$,一个数值$m$,一个符号$k$(1表示&,2表示|,3表示^),将点$u$的点权修改为$m$,符号修改成$k$。

    2、给定一条路径的两个端点(有可能是同一个点)$u、v$,再给定一个数值$k$,查询对于$[0,k]$中的每一个整数沿着从u到v的路径顺次进行运算后的最大值。

    点权$V_i < 2^{64}-1$

    题解

    树上支持修改点和查询路径信息,很容易让人联想到树链剖分。

    对于某一个数,经过这条确定的路径之后结果是确定的,但我们无法一次性解决所有数经过某条路径后的值,于是考虑拆位,对于每一位为$1$或$0$单独求出经过运算的值,这样不仅可以解决在运算过程中对于每一位的重复运算,还可在二进制下贪心地求出$[0,k]$中每个数经过运算的最大值。

    即,假设我们已经求出第$0$至$63$每一位为$0、1$时的结果,我们从高位向低位依次考虑,设一开始在$[0,k]$中选出的数为$0$,最终答案也是$0$,若当前位为$0$在运算之后变为$1$,则将最终答案加上这一位的真实值且选出的数不变,否则,若当前位为$1$时在运算之后变为$1$,而且我们目前选出的数加上当前位的值不超过$k$,就把最终答案和选出的数都加上当前位的值。再要不然就只能舍弃这一位,即这一位对最终答案无贡献。

    现在,问题只剩下如何用线段树维护某条路径的答案了,如果我们对每一位单独维护一颗线段树,那么复杂度位$O(64 imes mcdot log^2(n))$显然会$TLE$,由于降低树剖本身的复杂度几乎不可能,于是我们考虑优化那个$64$的常数。于是我们考虑再压位,因为$64$位$0$和$64$位$1$总共有$128$个结果,而每一个结果也是$0和1$,所以对于每一个线段树节点存$2$个$unsigned$ $long$ $long$,利用位运算的优秀复杂度来加速两个线段树节点的合并,最终复杂度为$O(64 imes m+mcdot log^2(n))$,注意路径有向所以要考虑两个方向。

    AC代码如下:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define ULL unsigned long long
    #define mid (l+r>>1)
    #define M 200010
    using namespace std;
    const ULL u1=1;
    ULL read(){
    	ULL nm=0,fh=1;char cw=getchar();
    	for(;!isdigit(cw);cw=getchar()) ;
    	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
    	return nm*fh;
    }
    ULL n,m,K,tp[M],fa[M],nt[M<<1],fs[M],to[M<<1],kd[M],val[M],MAX;
    ULL dfn[M],sz[M],mxs[M],u,v,dep[M],rt,tk[M],cnt,tpe,T,tmp=1;
    ULL calc(ULL num,ULL bas,ULL op){
    	if(op==1) return num&bas;
    	if(op==2) return num|bas;
    	return num^bas;
    }
    void dfs1(ULL x){
    	sz[x]=u1,mxs[x]=0;
    	ULL maxn=0;
    	for(ULL i=fs[x];i>0;i=nt[i]){
    		if(to[i]==fa[x]) continue;
    		fa[to[i]]=x,dep[to[i]]=dep[x]+u1;
    		dfs1(to[i]),sz[x]+=sz[to[i]];
    		if(sz[to[i]]>maxn) maxn=sz[to[i]],mxs[x]=to[i];
    	}
    }
    void dfs2(ULL x,ULL top){
    	dfn[x]=++cnt,tp[x]=top,tk[cnt]=x;
    	if(mxs[x]>0) dfs2(mxs[x],top);
    	for(ULL i=fs[x];i>0;i=nt[i]){
    		if(to[i]==mxs[x]||to[i]==fa[x]) continue;
    		dfs2(to[i],to[i]);
    	}
    }
    struct Seg{
    	ULL d[2][2];
    	void init(ULL vl,ULL op){d[0][0]=d[1][0]=calc(0,vl,op),d[0][1]=d[1][1]=calc(MAX,vl,op);}
    	void turn(){swap(d[0][0],d[1][0]),swap(d[0][1],d[1][1]);}
    	void merge(Seg L,Seg R){
    		d[0][0]=(L.d[0][0]&R.d[0][1])|((~L.d[0][0])&R.d[0][0]);
    		d[1][0]=(R.d[1][0]&L.d[1][1])|((~R.d[1][0])&L.d[1][0]);
    		d[0][1]=(L.d[0][1]&R.d[0][1])|((~L.d[0][1])&R.d[0][0]);
    		d[1][1]=(R.d[1][1]&L.d[1][1])|((~R.d[1][1])&L.d[1][0]);
    	}
    }p[M<<2],ans;
    void build(ULL x,ULL l,ULL r){
    	if(l==r) return p[x].init(val[tk[l]],kd[tk[l]]);
    	else build(x<<1,l,mid),build(x<<1|1,mid+1,r),p[x].merge(p[x<<1],p[x<<1|1]);
    }
    void change(ULL x,ULL l,ULL r,ULL pos,ULL op,ULL num){
    	if(l==r) return p[x].init(num,op);
    	if(pos<=mid) change(x<<1,l,mid,pos,op,num);
    	else change(x<<1|1,mid+1,r,pos,op,num);
    	p[x].merge(p[x<<1],p[x<<1|1]);
    }
    Seg query(ULL x,ULL l,ULL r,ULL L,ULL R){
    	if(L<=l&&r<=R) return p[x];
    	Seg sum; sum.init(0,3);
    	if(r<L||R<l) return sum;
    	sum.merge(query(x<<1,l,mid,L,R),query(x<<1|1,mid+1,r,L,R));
    	return sum;
    }
    ULL getans(ULL x,ULL y,ULL num){
    	Seg res1,res2,cur; ULL tot=0;
    	res1.init(0,3),res2.init(0,3);
    	while(tp[x]!=tp[y]){
    		if(dep[tp[x]]>dep[tp[y]]) res1.merge(query(1,1,n,dfn[tp[x]],dfn[x]),res1),x=fa[tp[x]];
    		else res2.merge(query(1,1,n,dfn[tp[y]],dfn[y]),res2),y=fa[tp[y]];
    	}
    	if(dep[x]>dep[y]) res1.merge(query(1,1,n,dfn[y],dfn[x]),res1);
    	else res2.merge(query(1,1,n,dfn[x],dfn[y]),res2);
    	res1.turn(),cur.merge(res1,res2);
    	for(ULL i=K;i-->0;){
    		if(cur.d[0][0]&(u1<<i)) tot+=(u1<<i);
    		else if((cur.d[0][1]&(u1<<i))>0&&num>=(u1<<i)) num-=(u1<<i),tot+=(u1<<i);
    	}
    	return tot;
    }
    void link(ULL x,ULL y){nt[tmp]=fs[x],fs[x]=tmp,to[tmp++]=y;}
    int main(){
    	n=read(),T=read(),K=read(),rt=(n+1>>1);
    	for(ULL i=0;i<K;i++) MAX+=(((ULL)1)<<i);
    	for(ULL i=1;i<=n;i++) kd[i]=read(),val[i]=read(),fs[i]=0;
    	for(ULL i=1;i<n;i++) u=read(),v=read(),link(u,v),link(v,u);
    	dfs1(rt),dfs2(rt,rt),build(1,1,n);
    	while(T--){
    		tpe=read(),u=read(),v=read(),m=read();
    		if(tpe==2) change(1,1,n,dfn[u],v,m);
    		else printf("%llu
    ",getans(u,v,m));
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    EF上下文容器,保存线程唯一性
    zabbix 监控服务器的TCP状态
    C++ 类里面,函数占用存储空间问题
    大道至简第一章读后感(伪代码)
    读大道至简有感
    String 部分源码分析
    LinkedList 源码分析
    ArrayList 源码分析
    定时取数据库的schema,并推送到git服务器
    全面解读python web 程序的9种部署方式
  • 原文地址:https://www.cnblogs.com/OYJason/p/9402229.html
Copyright © 2020-2023  润新知