1 条题解
-
0
自动搬运
来自洛谷,原作者为

Ebola
来证个定理吧!搬运于
2025-08-24 22:07:25,当前版本为作者最后更新于2019-02-18 11:42:46,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
考虑对每一个二进制位分别计算贡献。设表示异或和的位为的子集数量,那么令表示全集大小,表示位为的数有几个。显然符合条件的子集应恰好包含奇数个位为的数,其它随意。于是有:
$$cnt_k=2^{n-m_k}\times\sum_{i=1}^{\left\lfloor\frac{m_k}{2}\right\rfloor}\binom{m_k}{2i-1}=2^{n-m_k}\times2^{m_k-1}=2^{n-1} $$特别地,当时,
感觉似乎简洁的有点出乎意料?一个二进制位是否有贡献,只和是否为有关!
于是,一个集合的子异和就是:。不难看出这个求和运算实际上是在计算所有数的二进制或,所以该式可写作:
好了,现在询问已经解决了,只要求出路径上点的数量以及点权或就行。现在考虑修改
如果我们知道了一个集合的与、或,现在这个集合所有数要异或。那么,令、、分别表示或的位、与的位、的位,则不难得出:
$$\cup_{k[c_k==1]}'=\begin{cases}1&\cap_k==0\\0&\cap_k==1\end{cases},\qquad\cap_{k[c_k==1]}'=\begin{cases}1&\cup_k==0\\0&\cup_k==1\end{cases} $$进而得到:
所以说,我们需要维护路径与、路径或。树剖套线段树即可
#include<bits/stdc++.h> using namespace std; const int ha=1e9+7; const int N=200010; unsigned sand[N<<2],sor[N<<2],lazy[N<<2]; int dfn[N],idx[N],dfc=0; int fa[N],hson[N],top[N]; int dep[N],siz[N]; vector<int> g[N]; int n,m,val[N],pw2[N]; void update(int o,unsigned c) { unsigned a=sor[o],b=sand[o]; sor[o]=(a&~c)|(c&~b); sand[o]=(b&~c)|(c&~a); lazy[o]^=c; } void pushdown(int o) { if(!lazy[o]) return; update(o<<1,lazy[o]); update(o<<1|1,lazy[o]); lazy[o]=0; } void build(int o,int l,int r) { if(l==r){sand[o]=sor[o]=val[idx[l]];return;} int mid=(l+r)/2; build(o<<1,l,mid); build(o<<1|1,mid+1,r); sand[o]=sand[o<<1]&sand[o<<1|1]; sor[o]=sor[o<<1]|sor[o<<1|1]; } void cxor(int o,int l,int r,int nl,int nr,unsigned c) { if(l>=nl&&r<=nr){update(o,c);return;} int mid=(l+r)/2;pushdown(o); if(nl<=mid) cxor(o<<1,l,mid,nl,nr,c); if(nr>mid) cxor(o<<1|1,mid+1,r,nl,nr,c); sand[o]=sand[o<<1]&sand[o<<1|1]; sor[o]=sor[o<<1]|sor[o<<1|1]; } unsigned qor(int o,int l,int r,int nl,int nr) { if(l>=nl&&r<=nr) return sor[o]; int mid=(l+r)/2;unsigned res=0;pushdown(o); if(nl<=mid) res|=qor(o<<1,l,mid,nl,nr); if(nr>mid) res|=qor(o<<1|1,mid+1,r,nl,nr); return res; } void dfs1(int u) { siz[u]=1; for(int v : g[u]) { if(v==fa[u]) continue; dep[v]=dep[u]+1; fa[v]=u;dfs1(v); siz[u]+=siz[v]; if(siz[v]>siz[hson[u]]) hson[u]=v; } } void dfs2(int u,int tp) { top[u]=tp; idx[dfn[u]=++dfc]=u; if(hson[u]) dfs2(hson[u],tp); for(int v : g[u]) if(v!=fa[u]&&v!=hson[u]) dfs2(v,v); } void pathxor(int u,int v,unsigned c) { while(top[u]!=top[v]) { if(dep[top[u]]<dep[top[v]]) swap(u,v); cxor(1,1,n,dfn[top[u]],dfn[u],c); u=fa[top[u]]; } if(dep[u]>dep[v]) swap(u,v); cxor(1,1,n,dfn[u],dfn[v],c); } int query(int u,int v) { unsigned orsum=0,cnt=0; while(top[u]!=top[v]) { if(dep[top[u]]<dep[top[v]]) swap(u,v); orsum|=qor(1,1,n,dfn[top[u]],dfn[u]); cnt+=dfn[u]-dfn[top[u]]+1; u=fa[top[u]]; } if(dep[u]>dep[v]) swap(u,v); orsum|=qor(1,1,n,dfn[u],dfn[v]); cnt+=dfn[v]-dfn[u]+1; return 1ll*orsum*pw2[cnt-1]%ha; } int main() { scanf("%d%d",&n,&m); pw2[0]=1; for(int i=1;i<=n;i++) pw2[i]=(pw2[i-1]<<1)%ha;; for(int i=1,u,v;i<n;i++) { scanf("%d%d",&u,&v); g[u].emplace_back(v); g[v].emplace_back(u); } for(int i=1;i<=n;i++) scanf("%d",val+i); dfs1(1);dfs2(1,1); build(1,1,n); int opt,u,v;unsigned c; while(m--) { scanf("%d%d%u",&opt,&u,&v); if(opt==1) printf("%d\n",query(u,v)); else scanf("%u",&c),pathxor(u,v,c); } return 0; }
- 1
信息
- ID
- 3906
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- (无)
- 递交数
- 0
- 已通过
- 0
- 上传者