[bzoj 4811] 由乃的OJ(贪心 + 树链剖分)

前置技能:[Noi2014]起床困难综合症。

不难看出,这道题其实就是上一道题的加强版
在上一道题中,因为位运算时位与位之间互不干扰
所以从高位到低位枚举初始值二进制上的每一位为0和为1时,经过n次计算后这一位的结果,贪心选取
在这题中,我们也可以用同样的思路求取答案
因为有树上路径查询,考虑树链剖分,用线段树维护每一位初始为0和1时,经过一个区间的计算变为多少
因为&、|、^ 运算都符合结合律,所以这样做是可行的
问题是,&、|、^ 运算并不都满足交换律,即每个区间从左到右计算和从右到左计算得到的结果是不同的
而在树上从x走到y的途中,并不保证一定按dfn序从小到大的顺序走
所以我们还要分别维护每个区间从左到右计算和从右到左计算的结果
另外,给每一位都开一颗线段树显然是不现实的,
考虑到 k<=64,我们使用状压,把所有位都压到一起,存到一颗线段树中(注意要开unsigned long long)

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int N=100010;
ull read(){
	ull x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
struct Edge{
    int u,v,nxt;
}edge[N<<1];
int head[N],cnt;
void addedge(int u,int v){
    edge[++cnt]=(Edge){u,v,head[u]};head[u]=cnt;
    edge[++cnt]=(Edge){v,u,head[v]};head[v]=cnt;
}
int n,m,k;
int dep[N],son[N],sz[N],top[N],fa[N],tid[N],rnk[N],ind;
void dfs1(int u,int f){
    fa[u]=f;
    dep[u]=dep[f]+1;
    sz[u]=1;
    for(int i=head[u];~i;i=edge[i].nxt) 
		if(edge[i].v!=f){
        	dfs1(edge[i].v,u);
        	if(sz[edge[i].v]>sz[son[u]]) son[u]=edge[i].v;
        	sz[u]+=sz[edge[i].v];
    	}
}
void dfs2(int u,int tp){
    top[u]=tp;
    tid[u]=++ind;
    rnk[ind]=u;
    if(sz[u]>1) dfs2(son[u],tp);
    for(int i=head[u];~i;i=edge[i].nxt) 
		if(edge[i].v!=son[u]&&edge[i].v!=fa[u])
        	dfs2(edge[i].v,edge[i].v);
}
ull tran[N<<3][2][2];
//tran[o][0:带进0计算 ? 1:带进1计算][0:从左往右算 ? 1:从右往左算]
inline ull transf(ull a,ull b,int opt){
    if(opt==1){
        return a&b;
    }else if(opt==2){
        return a|b;
    }else return a^b;
}
int op;
inline ull transf(ull a,ull b){
    if(op==1){
        return a&b;
    }else if(op==2){
        return a|b;
    }else return a^b;
}
inline void pushup(int o){
    int ls=o<<1,rs=o<<1|1;
    tran[o][1][0]=(tran[ls][1][0]&tran[rs][1][0])|(~tran[ls][1][0]&tran[rs][0][0]);
    tran[o][1][1]=(tran[rs][1][1]&tran[ls][1][1])|(~tran[rs][1][1]&tran[ls][0][1]);
    tran[o][0][0]=(tran[ls][0][0]&tran[rs][1][0])|(~tran[ls][0][0]&tran[rs][0][0]);
    tran[o][0][1]=(tran[rs][0][1]&tran[ls][1][1])|(~tran[rs][0][1]&tran[ls][0][1]);
}
void update(int o,int l,int r,int x,ull k){
    if(l==r){
        tran[o][0][0]=tran[o][0][1]=transf(0ull,k);
        tran[o][1][0]=tran[o][1][1]=transf(~0ull,k);
    }
	else{
        int mid=l+r>>1;
        if(x<=mid) update(o<<1,l,mid,x,k);
        else update(o<<1|1,mid+1,r,x,k);
        pushup(o);  
    }
}
ull t1[2],t0[2];
int t;
void query(int o,int l,int r,int a,int b){
    if(a<=l&&b>=r){   
        if(!t){
            ull tt=t1[t];
            t1[t]=(tran[o][1][t]&t1[t])|(~tran[o][1][t]&t0[t]);
            t0[t]=(tran[o][0][t]&tt)|(~tran[o][0][t]&t0[t]);
        }else{
            t1[t]=(t1[t]&tran[o][1][t])|(~t1[t]&tran[o][0][t]);
            t0[t]=(t0[t]&tran[o][1][t])|(~t0[t]&tran[o][0][t]);     
        }
    }
	else{
        int mid=l+r>>1;
        if(b>mid) query(o<<1|1,mid+1,r,a,b);
        if(a<=mid) query(o<<1,l,mid,a,b);      
    }
}
ull val[N];
int opt[N];
void build(int o,int l,int r){
    if(l==r){   
        tran[o][0][0]=tran[o][0][1]=transf(0ull,val[rnk[l]],opt[rnk[l]]);
        tran[o][1][0]=tran[o][1][1]=transf(~0ull,val[rnk[l]],opt[rnk[l]]);
    }
	else{
        int mid=l+r>>1;
        build(o<<1,l,mid);
        build(o<<1|1,mid+1,r);
        pushup(o);
    }
}
ull z;
inline ull ask(int x,int y){
    t0[0]=t0[1]=0;
    t1[0]=t1[1]=~0ull;
    while(top[x]!=top[y]){
        if(dep[top[x]]>dep[top[y]]){
            t=1;
            query(1,1,n,tid[top[x]],tid[x]);
            x=fa[top[x]];
        }
		else{
            t=0;
            query(1,1,n,tid[top[y]],tid[y]);
            y=fa[top[y]];
        }
    }
    if(dep[x]>dep[y]){
        t=1;
        query(1,1,n,tid[y],tid[x]);     
    }
	else{
        t=0;
        query(1,1,n,tid[x],tid[y]);
    }
    ull T0=(t0[1]&t1[0])|(~t0[1]&t0[0]);
    ull T1=(t1[1]&t1[0])|(~t1[1]&t0[0]);
    ull v=0,ans=0;
    for(int i=k;~i;i--){
        ull v1=v|(1ull<<i);
        if(v1<=z){
            if(T0&(1ull<<i)||!(T1&(1ull<<i)))
                ans|=T0&(1ull<<i);
            else{
                ans|=T1&(1ull<<i);
                v|=(1ull<<i);
            }
        }
		else{
            ans|=T0&(1ull<<i);
        }
    }
    return ans;
}   
int main(){
    memset(head,cnt=-1,sizeof(head));
    n=read();m=read();k=read();k--;
    for(int i=1;i<=n;i++)
        opt[i]=read(),val[i]=read();
    for(int i=1;i<n;i++)
        addedge(read(),read());
    dfs1(1,1);
    dfs2(1,1);
    build(1,1,n);
    while(m--){
        int q=read(),x=read( ),y=read( );z=read( );
        if(q==1)  
            printf("%llu\n",ask(x,y));
        else{
            op=y;
            update(1,1,n,tid[x],z);
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值