[2016北京集训测试赛]奇怪的树-[树链剖分]

Posted coco-night

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[2016北京集训测试赛]奇怪的树-[树链剖分]相关的知识,希望对你有一定的参考价值。

Description

技术分享图片

Solution

对于操作1,不论选了哪个点为a,最后反转颜色的点集都只有两种类型(显然啦)。

暴力解法:对每个操作3,从a向上直到根节点,每到一个节点记录(它父亲的黑点数减去自己的黑点数)*父亲节点的编号。另外,还要记录a子树内的黑点。。

这种O(n2)的做法肯定会爆,考虑优化。由于这是一棵静态树,考虑树链剖分。

需要记录一个数组re[x][0/1][0/1]。第2维表示深度的奇偶,第3维表示点的颜色。例如:re[x][0][0]记录的是初始情况下以x为根的子树中深度为偶数的点有多少个为白色。

为了能够顺利剖分,需要记录一个num[0/1][0/1],它是树状数组,同样一维为深度,一维为颜色。num[0][0].tree[dfn[x]](这里的tree[dfn[x]]是单纯这个节点的值而不是该点所表示区间的值)表示的是(除了x的重儿子外其他孩子子树中深度为偶数的点为白色的个数)*x。(在计算途中,假如有操作1,则num[0][0]或者num[1][0]的定义可能会改变,即num[0/1][0]最后一维的定义可能由白色变为黑色,需要开一个数组flag[2]记录)

最后,我们还需要一个树状数组sum[0/1][0/1],两维所表示意义同上。它记录单独某个点的颜色。那知道了某个点x在dfs2中的dfn(也可以把它理解为in)和out后,就可以用sum查询x点子树内有多少个深度为奇(偶)的点颜色为白(黑)。(PS:当有操作1时sum的定义也可能改变,num和sum的定义是一起变的,所以只开一个数组flag记录就好)

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const long long MAXN=200100;
 
struct dd{long long y,next1;
}g[2*MAXN];
 
long long m;
long long re[MAXN][2][2],h[MAXN],size[MAXN],tot,jsq,n,val[MAXN];
long long fa[MAXN],son[MAXN],dep[MAXN],top[MAXN];
long long out[MAXN],dfn[MAXN];bool flag[2];
struct tree
{
    long long c[MAXN];
    inline long long lowbit(long long x){ return x&-x;
    }   
    inline void add(long long x,long long k)
    {   for (;x<=n;x+=lowbit(x)) c[x]+=k;
    }
    inline long long find(long long x)
    {long long ans1=0;  
    for (;x>=1;x-=lowbit(x)) ans1+=c[x];return ans1;
    }
    inline long long query(long long l,long long r)
    {
        return find(r)-find(l-1);
    }
}sum[2][2],num[2][2];
void dfs1(long long x,long long fa1)
{
    fa[x]=fa1;
    long long maxx=0,id=0;
    dep[x]=dep[fa1]+1;
    size[x]=1;
    re[x][dep[x]&1][val[x]]=1;
    for (long long i=h[x];i!=-1;i=g[i].next1)
    {
        if (g[i].y==fa[x]) continue;
        dfs1(g[i].y,x);
        size[x]+=size[g[i].y];
        for (long long j=0;j<=1;j++) for (long long k=0;k<=1;k++) re[x][j][k]+=re[g[i].y][j][k];
        if (size[g[i].y]>maxx) maxx=size[g[i].y],id=g[i].y;
    }
    son[x]=id;
}
void dfs2(long long x,long long u)
{
    top[x]=u;
    jsq++;dfn[x]=jsq;
    sum[dep[x]&1][val[x]].add(dfn[x],1);
    if (son[x]!=0) dfs2(son[x],u);
    for (long long i=h[x];i!=-1;i=g[i].next1)
    {
        if (g[i].y==fa[x]||g[i].y==son[x]) continue;
        dfs2(g[i].y,g[i].y);
    }
    out[x]=jsq;
    for (long long i=0;i<=1;i++)
        for (long long j=0;j<=1;j++) num[i][j].add(dfn[x],(re[x][i][j]-re[son[x]][i][j])*x);
}
int main()
{
    scanf("%lld%lld",&n,&m);
    memset(sum,0,sizeof(sum));
    memset(num,0,sizeof(num));
    memset(re,0,sizeof(re));
    memset(h,-1,sizeof(h));
    long long a,b,t;
    tot=jsq=0;
    for (long long i=1;i<=n;i++) scanf("%lld",&val[i]);
    for (long long i=1;i<n;i++)
    {
        scanf("%lld%lld",&a,&b);
        tot++;g[tot].y=b;g[tot].next1=h[a];h[a]=tot;
        tot++;g[tot].y=a;g[tot].next1=h[b]; h[b]=tot;
    }
    dfs1(1,0);dfs2(1,1);
    flag[0]=flag[1]=false;
    for (long long i=1;i<=m;i++)
    {
        scanf("%lld%lld",&t,&a);
        long long le;le=a;
        if (t==1) flag[(dep[a]&1)^1]^=1;
        if (t==2)
        {
            sum[dep[a]&1][val[a]].add(dfn[a],-1);
            while (a>0) num[dep[le]&1][val[le]].add(dfn[a],-a),a=fa[top[a]];
            a=le;     val[a]^=1;
            sum[dep[a]&1][val[a]].add(dfn[a],1);
            while (a>0) num[dep[le]&1][val[le]].add(dfn[a],a),a=fa[top[a]];
        }
        if (t==3)
        {
            long long res,ans;
            res=sum[0][flag[0]^1].query(dfn[a],out[a])+sum[1][flag[1]^1].query(dfn[a],out[a]);
            ans=res*a;
            while (a!=0)
            {
                if (top[a]!=a) ans+=num[0][flag[0]^1].query(dfn[top[a]],dfn[a]-1)+num[1][flag[1]^1].query(dfn[top[a]],dfn[a]-1),a=top[a];
                if (fa[a]==0) break;
                res=sum[0][flag[0]^1].query(dfn[fa[a]],out[fa[a]])+sum[1][flag[1]^1].query(dfn[fa[a]],out[fa[a]]);
                res-=sum[0][flag[0]^1].query(dfn[a],out[a])+sum[1][flag[1]^1].query(dfn[a],out[a]);
                ans+=res*fa[a];
                a=fa[a];
            }
            printf("%lld
",ans);
        }
    }
}

 


以上是关于[2016北京集训测试赛]奇怪的树-[树链剖分]的主要内容,如果未能解决你的问题,请参考以下文章

UOJ268 [清华集训2016] 数据交互 动态DP堆树链剖分线段树

学习笔记::lct

Codeforces 343D Water Tree & 树链剖分教程

并不对劲的树链剖分

LibreOJ #139 树链剖分 [树链剖分,线段树]

UVA - 12424 Answering Queries on a Tree(十棵线段树的树链剖分)