P3401 洛谷树 题解

2024/05/17 Blog

隐形霉菌死于此树下。

解法

考虑树链剖分,首先先将父亲与儿子间的边权转化为儿子的点权。

根据异或的性质,$a \oplus a = 0$,我们记每个节点到根的边权异或和为 $val_i$,任意两点间的边权异或和为 $dis_{i,j}$,这样,我们可以将树上任意两点 $x,y$ 间边权的异或和表示为 $val_x \oplus val_y$。考虑如下证明:$val_x \oplus val_y = val_x \oplus val_{lca} \oplus val_{lca} \oplus val_y = dis_{x,lca} \oplus dis_{lca,y} = dis_{x,y}$,其中 $lca$ 表示 $x$ 和 $y$ 的最近公共祖先。

原问题被转化为 $x$ 到 $y$ 间路径上的任意两点间的异或和的和。

注意到交换求和顺序是不影响答案的,并且此题值域很小,所以我们将题目从每个点对的每一个二进制位的贡献转化为了每一个二进制位上每个点对的贡献。

考虑异或的性质,只有 $0 \oplus 1 = 1$,即一个点对的某个二进制位不同时这个点对在这个二进制位上才有贡献,我们可以使用线段树维护每一个二进制位上有 $cnt0$ 个 $0$ 和 $cnt1$ 个 $1$,根据简单的组合,这个二进制位有 $cnt0 \times cnt1$ 次贡献,对和的贡献为 $cnt0 \times cnt1 \times 2^i$。

线段树维护 $val$ 的值即可。

至于修改操作,我们设节点 $x$ 原来的点权(我们已经将边权转化为了点权)为 $w_x$,要修改成的值为 $y$,那新的 $val_x$ 即为 $val_x \oplus w_x \oplus y$,异或 $w_x$ 的目的是撤销曾经 $w_x$ 对 $val_x$ 的贡献。注意到这个修改会影响到节点 $x$ 的子树的 $val$ 值,所以,我们对其子树进行同样的操作即可。

操作一为树链剖分,线段树区间查询,操作二为子树操作,线段树区间修改。

代码

#include<bits/stdc++.h>
namespace fast_IO
{
    /**
     * 屎山快读快写
    */
};
using namespace fast_IO;
const int N=30010;
int n,m,dep[N],fa[N],siz[N],hvson[N],id[N],times,start[N],w[N],neww[N],v[N],val[10];
struct edge
{
    int to,cost;
    edge *next;
};
edge *head[N];
inline void add(const int x,const int y,const int z)
{
    edge *e=new edge;
    e->to=y,e->next=head[x],e->cost=z,head[x]=e;
}
inline void dfs1(int pos,int f,int depth,int maxi=0)
{
    fa[pos]=f,dep[pos]=depth,siz[pos]=1;
    for(edge *i=head[pos];i!=nullptr;i=i->next)
        if(i->to!=f)
        {
            w[i->to]=w[pos]^i->cost,v[i->to]=i->cost,dfs1(i->to,pos,depth+1),siz[pos]+=siz[i->to];
            if(siz[i->to]>maxi) maxi=siz[i->to],hvson[pos]=i->to;
        }
}
inline void dfs2(int pos,int Start)
{
    start[pos]=Start,id[pos]=++times,neww[times]=w[pos];
    if(hvson[pos]) dfs2(hvson[pos],Start);
    for(edge *i=head[pos];i!=nullptr;i=i->next)
        if(i->to!=fa[pos] && i->to!=hvson[pos]) dfs2(i->to,i->to);
}
struct node
{
    int cnt0[10],cnt1[10],lazy[10];
    node *lc,*rc;
    inline node()
    {
        memset(cnt0,0,sizeof(cnt0)),memset(cnt1,0,sizeof(cnt1)),memset(lazy,0,sizeof(lazy));
        lc=rc=nullptr;
    }
    inline void pushup()
    {
        for(int i=0;i<10;i++) cnt0[i]=lc->cnt0[i]+rc->cnt0[i],cnt1[i]=lc->cnt1[i]+rc->cnt1[i];
    }
    inline void pushdown()
    {
        for(int i=0;i<10;i++)
            if(lazy[i])
            {
                std::swap(lc->cnt0[i],lc->cnt1[i]),std::swap(rc->cnt0[i],rc->cnt1[i]);
                lc->lazy[i]^=1,rc->lazy[i]^=1,lazy[i]=0;
            }
    }
};
class seg_tree
{
    #define ls l,mid
    #define rs mid+1,r
private:
    node *root;
    inline node *build(int l,int r)
    {
        node *rt=new node();
        if(l==r) for(int i=0;i<10;i++) rt->cnt1[i]=((neww[l]>>i)&1),rt->cnt0[i]=rt->cnt1[i]^1;
        else
        {
            int mid=(l+r)/2;
            rt->lc=build(ls),rt->rc=build(rs),rt->pushup();
        }
        return rt;
    }
    inline void fix(node *rt,const int L,const int R,int l,int r)
    {
        if(L<=l && r<=R)
        {
            for(int i=0;i<10;i++)
                if(val[i])
                    std::swap(rt->cnt0[i],rt->cnt1[i]),rt->lazy[i]^=1;
            return;
        }
        int mid=(l+r)/2;
        rt->pushdown();
        if(L<=mid) fix(rt->lc,L,R,ls);
        if(R>mid) fix(rt->rc,L,R,rs);
        rt->pushup();
    }
    inline node ask(node *rt,const int L,const int R,int l,int r)
    {
        if(L<=l && r<=R) return *rt;
        int mid=(l+r)/2;
        rt->pushdown();
        if(L>mid) return ask(rt->rc,L,R,rs);
        if(R<=mid) return ask(rt->lc,L,R,ls);
        node ll=ask(rt->lc,L,R,ls),rr=ask(rt->rc,L,R,rs),ret;
        ret.lc=&ll,ret.rc=&rr,ret.pushup();
        return ret;
    }
public:
    inline void build()
    {
        root=build(1,n);
    }
    inline void fix(const int L,const int R)
    {
        fix(root,L,R,1,n);
    }
    inline node ask(const int L,const int R)
    {
        return ask(root,L,R,1,n);
    }
};
seg_tree tree;
inline long long ask(int x,int y)
{
    long long ret=0;
    long long cnt0[10],cnt1[10];
    memset(cnt0,0,sizeof(cnt0)),memset(cnt1,0,sizeof(cnt1));
    node tmp;
    while(start[x]!=start[y])
    {
        if(dep[start[x]]<dep[start[y]]) std::swap(x,y);
        tmp=tree.ask(id[start[x]],id[x]),x=fa[start[x]];
        for(int i=0;i<10;i++) cnt0[i]+=tmp.cnt0[i],cnt1[i]+=tmp.cnt1[i];
    }
    if(dep[x]>dep[y]) std::swap(x,y);
    tmp=tree.ask(id[x],id[y]);
    for(int i=0;i<10;i++) cnt0[i]+=tmp.cnt0[i],cnt1[i]+=tmp.cnt1[i];
    for(int i=0;i<10;i++) ret+=cnt0[i]*cnt1[i]*(1ll<<i);
    return ret;
}
int main()
{
    in>>n>>m;
    for(int i=1,x,y,z;i<n;i++) in>>x>>y>>z,add(x,y,z),add(y,x,z);
    dfs1(1,0,1),dfs2(1,1),tree.build();
    for(int i=1,op,x,y,z;i<=m;i++)
    {
        in>>op>>x>>y;
        if(op==1) out<<ask(x,y)<<'\n';
        else if(op==2)
        {
            in>>z;
            if(dep[x]<dep[y]) std::swap(x,y);
            memset(val,0,sizeof(val));
            for(int j=0;j<10;j++) if(((z>>j)&1)!=((v[x]>>j)&1)) val[j]=1;
            tree.fix(id[x],id[x]+siz[x]-1),v[x]=z;
        }
    }
    fwrite(Ouf,1,p3-Ouf,stdout),fflush(stdout);
    return 0;
}
对我博客最大的鼓励来自于你的评论(什么玩梗),欢迎选择 来回复, 也可以在 GitHub discussion 留言。

Search

    公告

    博主这下真摆烂了。我下辈子也不碰 Jekyll 和 MathJax 了!
    留言板,现在博主的精神状态非常好,有事烧纸。

    联系方式

    QQ: 33299235
    email: 33299235@qq.com
    Gmail: scp020.cn@gmail.com

    本人的其它个人主页

    洛谷
    CSDN

    友链

    拜谢stevehim(已退役,祝好)
    拜谢日记
    拜谢cppomstar

    views counting

    Document

    Table of Contents