1. 程式人生 > >BZOJ_3589_動態樹_容斥原理+樹鏈剖分

BZOJ_3589_動態樹_容斥原理+樹鏈剖分

query using mod mask tin 容斥原理 urn con pos

BZOJ_3589_動態樹_容斥原理+樹鏈剖分

題意:

維護一棵樹,支持1.子樹內點權加上一個數 2.給出k條鏈,求路徑上的點權和(重復的計算一次) (k<=5)

分析:

可以用樹剖+線段樹解決第一個操作

然後我們發現k非常小,可以二進制枚舉

那就容斥一下轉化成求幾條鏈的交

鏈交求法:鏈頂是兩條鏈頂深度大的那個,鏈底是兩個鏈底的lca

如果鏈底深度小於鏈頂,就說明兩條鏈沒有交集

代碼:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 200050
#define LL long long
#define ls p<<1
#define rs p<<1|1
int head[N],to[N<<1],nxt[N<<1],cnt,n,q,xx[10],yy[10];
int fa[N],dep[N],top[N],siz[N],son[N],idx[N],tot,k;
int _count[100],strtop[10],strbot[10];
LL mod=2147483648ll;
LL t[N<<2],add[N<<2];
inline void adde(int u,int v){
    to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
}
void dfs1(int x,int y){
    int i;
    fa[x]=y;dep[x]=y+1;
    siz[x]=1;
    for(i=head[x];i;i=nxt[i]){
        if(to[i]!=y){
            dfs1(to[i],x);
            siz[x]+=siz[to[i]];
            if(siz[to[i]]>siz[son[x]])son[x]=to[i];
        }
    }
}
void dfs2(int x,int t){
    top[x]=t;idx[x]=++tot;
    int i;
    if(son[x]) dfs2(son[x],t);
    for(i=head[x];i;i=nxt[i]){
        if(to[i]!=fa[x]&&to[i]!=son[x]){
            dfs2(to[i],to[i]);
        }
    }
}
void pud(int l,int r,int p){
    if(add[p]==0)return ;
    add[ls]+=add[p];
    add[ls]%=mod;
    add[rs]+=add[p];
    add[rs]%=mod;
    int mid=l+r>>1;
    t[ls]+=add[p]*(mid-l+1);
    t[ls]%=mod;
    t[rs]+=add[p]*(r-mid);
    t[rs]%=mod;
    add[p]=0;
}
void update(int l,int r,int x,int y,int c,int p){
    if(x<=l&&y>=r){
        t[p]+=1ll*c*(r-l+1);
        add[p]+=c;
        t[p]%=mod;
        add[p]%=mod;
        return ;
    }
    pud(l,r,p);
    int mid=l+r>>1;
    if(x<=mid)update(l,mid,x,y,c,ls);
    if(y>mid)update(mid+1,r,x,y,c,rs);
    t[p]=t[ls]+t[rs];
    t[p]%=mod;
}
LL query(int l,int r,int x,int y,int p){
    if(x<=l&&y>=r) return t[p];
    int mid=l+r>>1;
    LL re=0;
    pud(l,r,p);
    t[p]=t[ls]+t[rs];
    t[p]%=mod;
    if(x<=mid)re=(re+query(l,mid,x,y,ls))%mod;
    if(y>mid)re=(re+query(mid+1,r,x,y,rs))%mod;
    return re%mod;
}
LL ask(int x,int y){
    LL re=0;
    while(top[x]!=top[y]){
        if(dep[top[x]]>dep[top[y]])swap(x,y);
        re+=query(1,n,idx[top[y]],idx[y],1);
        re%=mod;
        y=fa[top[y]];
    }
    if(dep[x]<dep[y])swap(x,y);
    return (re+query(1,n,idx[y],idx[x],1))%mod;
}
void fix(int x,int c){
    update(1,n,idx[x],idx[x]+siz[x]-1,c,1);
}
int lca(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]>dep[top[y]])swap(x,y);
        y=fa[top[y]];
    }
    return dep[x]<dep[y]?x:y;
}
void solve(){
    int mask=(1<<k)-1;
    int i,flg,j;
    LL ans=0;
    for(i=1;i<=k;i++){
        if(dep[xx[i]]>dep[yy[i]])swap(xx[i],yy[i]);
        strtop[i]=xx[i];strbot[i]=yy[i];
    }
    for(i=1;i<=mask;i++){
        if((_count[i]&1))flg=1;
        else flg=-1;
        int no_jiao=0;
        int top_a=0,bot_a=0;
        for(j=1;j<=k;j++){
            if(i&(1<<j-1)){
                if(!top_a){
                    top_a=strtop[j];
                    bot_a=strbot[j];
                }
                else {
                    bot_a=lca(bot_a,strbot[j]);
                    if(dep[top_a]<dep[strtop[j]]){
                        top_a=strtop[j];
                    }
                    if(dep[top_a]>dep[bot_a])no_jiao=1;
                }
            }
        }
        if(no_jiao)continue;
        ans+=flg*ask(top_a,bot_a);
        ans=(ans+mod)%mod;
    }
    printf("%lld\n",ans);
}
int main(){
    scanf("%d",&n);
    int i,x,y,opt,j;
    for(i=1;i<n;i++){
        scanf("%d%d",&x,&y);
        adde(x,y);adde(y,x);
    }
    dfs1(1,0);
    dfs2(1,1);
    for(i=1;i<=32;i++){
        _count[i]=_count[i>>1]+(i&1);
    }
    scanf("%d",&q);
    while(q--){
        scanf("%d",&opt);
        if(opt){
            scanf("%d",&k);
            for(j=1;j<=k;j++){
                scanf("%d%d",&xx[j],&yy[j]);
            }
            solve();
        }else{
            scanf("%d%d",&x,&y);
            fix(x,y);
        }
    }
}

BZOJ_3589_動態樹_容斥原理+樹鏈剖分