1. 程式人生 > >【洛谷】T46495 子異和 -拆位找規律&線段樹

【洛谷】T46495 子異和 -拆位找規律&線段樹

傳送門:luoguT46495 子異和


題解

這題的性質和維護都很妙啊。

一個數集的子異和為其所有非空子集的集合異或和之和。

考慮如何 O ( n ) O(n) 回答單次詢問:
拆位後用桶 t

[ i ] [ x ] t[i][x] 表示第 x
x
位在前 i i 個數組成的所有非空子集中出現的總次數。

可以把前 i i 個數組成的所有非空子集分成含有 a

i a_i 和不含有 a i a_i 兩部分,分別處理後加起來:

  • 若當前數 a i a_i 二進位制第 x x 位為 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}
  • 若當前數 a i a_i 二進位制第 x x 位為 0 0 : t [ i ] [ x ] = t [ i 1 ] [ x ] × 2 t[i][x]=t[i-1][x]\times 2

顯然能夠得到結論:集合 S = a 1 , a 2 , . . , a S S={a_1,a_2,..,a_{|S|}} 的子異和 = ( a 1 a 2 . . . a S ) × 2 S 1 =(a_1|a_2|...|a_{|S|})\times 2^{|S|-1}

對於詢問, d f s dfs 序+線段樹維護區間異或和即可。

為了維護每次修改後的異或和,還需要維護區間與,再深入發掘一下性質:

對於一個區間的數異或上一個 c c 時,觀察到:

0 0 變成 1 1 的位必然滿足 c c 的二進位制對應位為 1 1 ,且區間或值對應位為 0 0
1 1 變成 0 0 的位必然滿足 c c 的二進位制對應位為 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;
}