#树链剖分,线段树#洛谷 5127 子异和

题目

多组询问,每次把树上的一条简单路径中的点权扔入集合中,求该集合的子异和,子异和定义为所有非空子集的异或和的和,而且需要支持区间异或


分析

考虑某一位满足异或和为1的子集数,若设异或某一位为 k k k为1的数量为 h k h_k hk,那么结果应为 c n t k = 2 n − h k × ∑ i = 1 ⌊ h k 2 ⌋ C ( n , 2 i − 1 ) = 2 n − 1 cnt_k=2^{n-h_k}\times \sum_{i=1}^{\lfloor\frac{h_k}{2}\rfloor}C(n,2i-1)=2^{n-1} cntk=2nhk×i=12hkC(n,2i1)=2n1,竟然只与 h k hk hk是否为0有关,最终答案竟然变成了 2 n − 1 × o r s u m 2^{n-1}\times orsum 2n1×orsum,但是如何实现区间异或,显然只有或是不够的,所以我们可能要引入与,分类讨论,其实也很好理解的呀

t a n d = ( t a n d & ! z ) ∣ ( ! t o r & z ) , t o r = ( ! t a n d & z ) ( t o r & ! z ) t_{and}=(t_{and}\& !z)|(!t_{or}\& z),t_{or}=(!t_{and}\&z)(t_{or}\& !z) tand=(tand&!z)(!tor&z),tor=(!tand&z)(tor&!z)

所以就树剖套线段树解决


代码

#include <cstdio>
#include <cctype>
#define rr register
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<13,stdin)),p1==p2?EOF:*p1++)
using namespace std;
typedef unsigned uit; const uit N=200101,mod=1000000007; struct node{uit y,next;}e[N<<1]; char buf[1<<13],*p1,*p2;
uit wnd[N<<2],wor[N<<2],p[N],lazy[N<<2],ls[N],n,tot,K=1,a[N],A[N],top[N],dep[N],fat[N],dfn[N],son[N],big[N];
inline uit iut(){
	rr uit ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(uit ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
inline uit mo(uit x,uit y){return x+y>=mod?x+y-mod:x+y;}
inline void change(uit k,uit z){
	rr uit t1=wnd[k],t2=wor[k];
	wnd[k]=(t1&~z)|(~t2&z),
	wor[k]=(~t1&z)|(t2&~z);
	lazy[k]^=z;
}
inline void pdown(uit k){
	if (!lazy[k]) return;
	change(k<<1,lazy[k]),
	change(k<<1|1,lazy[k]);
	lazy[k]=0;
}
inline void build(uit k,uit l,uit r){
	if (l==r){wnd[k]=wor[k]=a[l]; return;}
	rr uit mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	wnd[k]=wnd[k<<1]&wnd[k<<1|1],wor[k]=wor[k<<1]|wor[k<<1|1];
}
inline uit query(uit k,uit l,uit r,uit x,uit y){
	if (l==x&&r==y) return wor[k];
	pdown(k); rr uit mid=(l+r)>>1;
	if (y<=mid) return query(k<<1,l,mid,x,y);
	else if (x>mid) return query(k<<1|1,mid+1,r,x,y);
	else return query(k<<1,l,mid,x,mid)|query(k<<1|1,mid+1,r,mid+1,y);
}
inline void update(uit k,uit l,uit r,uit x,uit y,uit z){
	if (l==x&&r==y) {change(k,z); return;}
	pdown(k); rr uit mid=(l+r)>>1;
	if (y<=mid) update(k<<1,l,mid,x,y,z);
	else if (x>mid) update(k<<1|1,mid+1,r,x,y,z);
	else update(k<<1,l,mid,x,mid,z),update(k<<1|1,mid+1,r,mid+1,y,z);
	wnd[k]=wnd[k<<1]&wnd[k<<1|1],wor[k]=wor[k<<1]|wor[k<<1|1];
}
inline void Update(uit x,uit y,uit z){
	while (top[x]!=top[y]){
		if (dep[top[x]]<dep[top[y]]) x^=y,y^=x,x^=y;
		update(1,1,n,dfn[top[x]],dfn[x],z),x=fat[top[x]];
	}
	if (dep[x]>dep[y]) x^=y,y^=x,x^=y;
	update(1,1,n,dfn[x],dfn[y],z);
}
inline uit Query(uit x,uit y){
	rr uit ans=0,cnt=0;
	while (top[x]!=top[y]){
		if (dep[top[x]]<dep[top[y]]) x^=y,y^=x,x^=y;
		ans|=query(1,1,n,dfn[top[x]],dfn[x]);
		cnt+=dfn[x]-dfn[top[x]]+1,x=fat[top[x]];
	}
	if (dep[x]>dep[y]) x^=y,y^=x,x^=y;
	cnt+=dfn[y]-dfn[x]+1,ans|=query(1,1,n,dfn[x],dfn[y]);
	return 1ll*ans*p[cnt-1]%mod;
}
inline void dfs1(uit x,uit fa){
	dep[x]=dep[fa]+1,fat[x]=fa,son[x]=1;
	for (rr uit i=ls[x],mson=0;i;i=e[i].next)
	if (e[i].y!=fa){
		dfs1(e[i].y,x);
		son[x]+=son[e[i].y];
		if (son[e[i].y]>mson) big[x]=e[i].y,mson=son[e[i].y];
	}
}
inline void dfs2(uit x,uit linp){
	dfn[x]=++tot,a[tot]=A[x],top[x]=linp;
	if (!big[x]) return; dfs2(big[x],linp);
	for (rr uit i=ls[x];i;i=e[i].next)
	if (e[i].y!=fat[x]&&e[i].y!=big[x]) dfs2(e[i].y,e[i].y);
}
signed main(){
	n=iut(); rr int m=iut(); p[0]=1;
	for (rr uit i=1;i<n;++i){
		rr uit x=iut(),y=iut();
		e[++K]=(node){y,ls[x]},ls[x]=K;
		e[++K]=(node){x,ls[y]},ls[y]=K;
	}
	for (rr uit i=1;i<=n;++i) A[i]=iut(),p[i]=mo(p[i-1],p[i-1]);
    dfs1(1,0),dfs2(1,1),build(1,1,n);
    while (m--){
    	rr uit opt=iut(),x=iut(),y=iut(),w;
    	if ((opt&1)^1) w=iut();
    	if (opt&1) print(Query(x,y)),putchar(10);
    	    else Update(x,y,w);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值