传送门:luoguT46495 子异和
题解
这题的性质和维护都很妙啊。
一个数集的子异和为其所有非空子集的集合异或和之和。
考虑如何
O
(
n
)
O(n)
O(n)回答单次询问:
拆位后用桶
t
[
i
]
[
x
]
t[i][x]
t[i][x]表示第
x
x
x位在前
i
i
i个数组成的所有非空子集中出现的总次数。
可以把前 i i i个数组成的所有非空子集分成含有 a i a_i ai和不含有 a i a_i ai两部分,分别处理后加起来:
- 若当前数 a i a_i ai二进制第 x x x位为 1 1 1: t [ i ] [ x ] = t [ i − 1 ] [ x ] + ( 2 i − 1 − 1 − t [ i − 1 ] [ x ] ) + 1 = 2 i − 1 t[i][x]=t[i-1][x]+(2^{i-1}-1-t[i-1][x])+1=2^{i-1} t[i][x]=t[i−1][x]+(2i−1−1−t[i−1][x])+1=2i−1
- 若当前数 a i a_i ai二进制第 x x x位为 0 0 0: t [ i ] [ x ] = t [ i − 1 ] [ x ] × 2 t[i][x]=t[i-1][x]\times 2 t[i][x]=t[i−1][x]×2
显然能够得到结论:集合 S = a 1 , a 2 , . . , a ∣ S ∣ S={a_1,a_2,..,a_{|S|}} S=a1,a2,..,a∣S∣的子异和 = ( a 1 ∣ a 2 ∣ . . . ∣ a ∣ S ∣ ) × 2 ∣ S ∣ − 1 =(a_1|a_2|...|a_{|S|})\times 2^{|S|-1} =(a1∣a2∣...∣a∣S∣)×2∣S∣−1
对于询问, d f s dfs dfs序+线段树维护区间异或和即可。
为了维护每次修改后的异或和,还需要维护区间与,再深入发掘一下性质:
对于一个区间的数异或上一个 c c c时,观察到:
从
0
0
0变成
1
1
1的位必然满足
c
c
c的二进制对应位为
1
1
1,且区间或值对应位为
0
0
0。
从
1
1
1变成
0
0
0的位必然满足
c
c
c的二进制对应位为
1
1
1,且区间与值对应位为
1
1
1。
这样维护就好了。
代码
#include<bits/stdc++.h>
#define mid ((l+r)>>1)
#define lc k<<1
#define rc k<<1|1
using namespace std;
typedef long long ll;
const int N=2e5+100,mod=1e9+7;
const int maxn=(1LL<<31)-1;
int n,m,val[N],bin[N];
int head[N],to[N<<1],nxt[N<<1],tot;
int df[N],top[N],sz[N],son[N],f[N],dep[N],dfn;
int qx[N<<2],qy[N<<2],lzy[N<<2];
inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
inline int ad(int x,int y){x+=y;return x>=mod?x-mod:x;}
char cp,SS[100];
template<class yyy>
inline void rd(yyy &x)
{
cp=getchar();x=0;
for(;!isdigit(cp);cp=getchar());
for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
}
inline void ot(int x)
{
int re=0;
for(;(!re)||(x);x/=10) SS[++re]='0'+x%10;
for(;re;--re) putchar(SS[re]);
putchar('\n');
}
void dfs(int x)
{
sz[x]=1;
for(int j,i=head[x];i;i=nxt[i]){
j=to[i];if(j==f[x]) continue;
f[j]=x;dep[j]=dep[x]+1;dfs(j);
sz[x]+=sz[j];if(sz[son[x]]<sz[j]) son[x]=j;
}
}
void dfss(int x,int tpo)
{
top[x]=tpo;df[x]=++dfn;
if(!son[x]) return;
dfss(son[x],tpo);
for(int j,i=head[x];i;i=nxt[i]){
j=to[i];if(j==f[x] || j==son[x]) continue;
dfss(j,j);
}
}
void build(int k,int l,int r)
{
if(l==r) {qx[k]=qy[k]=val[l];return;}
build(lc,l,mid);build(rc,mid+1,r);
qx[k]=qx[lc]|qx[rc];
qy[k]=qy[lc]&qy[rc];
}
inline void trs(int k,int v)
{
lzy[k]^=v;int x=qx[k],y=qy[k];
x^=(qy[k]&v);x|=((maxn^qy[k])&v);
y^=(qy[k]&v);y|=((maxn^qx[k])&v);
qx[k]=x;qy[k]=y;
}
inline void pushdown(int k)
{
if(!lzy[k]) return;
trs(lc,lzy[k]);trs(rc,lzy[k]);
lzy[k]=0;
}
int gt(int k,int l,int r,int L,int R)
{
if(L<=l && r<=R) return qx[k];
pushdown(k);
if(R<=mid) return gt(lc,l,mid,L,R);
if(L>mid) return gt(rc,mid+1,r,L,R);
qx[k]=qx[lc]|qx[rc];
qy[k]=qy[lc]&qy[rc];
return (gt(lc,l,mid,L,R)|gt(rc,mid+1,r,L,R));
}
void modify(int k,int l,int r,int L,int R,int v)
{
if(L<=l && r<=R){trs(k,v);return;}
pushdown(k);
if(L<=mid) modify(lc,l,mid,L,R,v);
if(R>mid) modify(rc,mid+1,r,L,R,v);
qx[k]=qx[lc]|qx[rc];
qy[k]=qy[lc]&qy[rc];
}
inline int ask(int x,int y)
{
int re=0,cot=-1;
for(;top[x]!=top[y];x=f[top[x]]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
cot+=(dep[x]-dep[top[x]]+1);
re|=gt(1,1,n,df[top[x]],df[x]);
}
if(dep[x]<dep[y]) swap(x,y);
cot+=(dep[x]-dep[y]+1);
re|=gt(1,1,n,df[y],df[x]);
return (ll)re*bin[cot]%mod;
}
inline void cg(int x,int y,int z)
{
for(;top[x]!=top[y];x=f[top[x]]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
modify(1,1,n,df[top[x]],df[x],z);
}
if(dep[x]<dep[y]) swap(x,y);
modify(1,1,n,df[y],df[x],z);
}
int main(){
int i,j,op,x,y,z;
rd(n);rd(m);
for(i=1;i<n;++i){
rd(x);rd(y);lk(x,y);lk(y,x);
}
bin[0]=1;for(i=1;i<=n;++i) bin[i]=ad(bin[i-1],bin[i-1]);
dep[1]=1;dfs(1);dfss(1,1);
for(i=1;i<=n;++i) rd(val[df[i]]);
build(1,1,n);
for(;m;--m){
rd(op);rd(x);rd(y);
if(op==1) ot(ask(x,y));
else{rd(z);cg(x,y,z);}
}
return 0;
}