[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\n",ans);
        }
    }
}

原文地址:https://www.cnblogs.com/coco-night/p/9565171.html

时间: 2024-09-28 09:55:23

[2016北京集训测试赛(一)]奇怪的树-[树链剖分]的相关文章

【2016北京集训测试赛(八)】 crash的数列

Description 题解 题目说这是一个具有神奇特性的数列!这句话是非常有用的因为我们发现,如果套着这个数列的定义再从原数列引出一个新数列,它居然还是一样的...... 于是我们就想到了能不能用多点数列套着来加速转移呢? 但是发现好像太多数列套起来是可以烦死人的...... 我们就采用嵌套两次吧,记原数列为A,第一层嵌套为B,第二层嵌套为C. 我们其实可以发现一些规律,对于Ci,它对应了B中i的个数:对于Bi,它对应了A中i的个数. 稍加处理即可,我们一边计算一边模拟数列的运算,同时可以计算

[2016北京集训测试赛17]crash的游戏-[组合数+斯特林数+拉格朗日插值]

Description Solution 核心思想是把组合数当成一个奇怪的多项式,然后拉格朗日插值..:哦对了,还要用到第二类斯特林数(就是把若干个球放到若干个盒子)的一个公式: $x^{n}=\sum _{i=0}^{n}C(n,i)*i!*S(i,x)$ 围观大佬博客(qaq公式太难打了) Code #include<iostream> #include<cstdio> #include<cstring> #include<cmath> using na

【2016北京集训测试赛(八)】直径

注意:时限更改为4s 题解 考虑最原始的直径求法:找到离根节点(或离其他任意一点)最远的节点far1,再从far1出发找到离far1最远的节点far2,far1至far2的距离即为直径. 题目中提到了将原树的子树复制成新子树这一操作,显然如果我们将子树真正复制出来是会爆炸的. 实际上我们可以将每棵新子树中,真正有用的节点提取出来,以简化每个子树的结构,再按照题目的要求连接各个新子树. 我们用虚树来重构每一棵子树.每棵子树的虚树的关键点应至少包含: 子树的根节点. 这棵子树内部的直径的两端节点.

【2016北京集训测试赛(七)】自动机 (思考题)

Time Limit: 1000 ms Memory Limit: 256 MB Description Solution 这是一道看起来令人毫无头绪的题,然而确实十分简单巧妙TAT. 题目要求所有点执行相同指令后都回到初始状态. 我们先来考虑只有两种状态的情况:初始状态$T_0$与另一个状态$T_x$. 这样,我们可以通过一个二元记忆化深搜,来得到一种方案A,使得$T_0$回到$T_0$,且$T_x$回到$T_0$.如果这个方案都不存在,那么此时无解. 现在我们知道,执行方案A后,$T_x$与

【2016北京集训测试赛】river

HINT 注意是全程不能经过两个相同的景点,并且一天的开始和结束不能用同样的交通方式. [吐槽] 嗯..看到这题的想法的话..先想到了每个点的度为2,然后就有点不知所措了 隐隐约约想到了网络流,但并没有继续往下想了... 听完学长的讲评之后(%xj)个人觉得建图还是很有意思的ovo [题解] 因为每个点到对面都有k种方式,那就想到每个点原来的点$x_0$拆成k个点$x_1$, $x_2$, $x_3$... $x_k$ 然后很自然地$x_0$和拆成的点之间要连边 容量的话,因为hint里面的限制

2016北京集训测试赛(十七)- 小结

先说一下总体的情况. 场上期望得分 50 + 40 + 30 = 120 , 最后得分 50 + 0 + 30 = 80 , 实际上自己能力所及能做到的 50 + 65 + 30 = 145 分. 第二题爆零是因为我开始写了一个做法, 后来发现这个做法是错的, 然后开始随机化, 调着调着突然发现只有一分钟了, 然后自己把之前调的改回来, 然后发现怎么全都输出 0 ??? Excuse me ?? 原本不用随机化的做法可以拿 40 分, 如果结合上暴力就有 65 了. 这几天打起比赛来还是暴露了许

【2016北京集训测试赛】azelso(unfinished)

[吐槽] 首先当然是要orzyww啦 以及orzyxq奇妙顺推很强qwq 嗯..怎么说呢虽然说之前零零散散做了一些概d的题目但是总感觉好像并没有弄得比较明白啊..(我的妈果然蒟蒻) 这题的话可以说是难得的一道搞得比较清楚的概d题目吧记录一下还是挺有意思的ovo 当然咯..显然考场上并没有推出来..嗯qwq [题解] 看到说要求期望的距离,然后总的长度又被分成了一段一段的(各个事件) 所以就有一个比较直接的想法:将每一段期望走的次数算出来然后再乘上每一段的距离,加起来就是答案啦 那么现在问题来了怎

[2016北京集训测试赛(五)]打地鼠-[思考题]

Description Solution 我们先只考虑一只地鼠的情况,依题意得,在某一个时刻该地鼠的可能停留位置是一个公差为2的等差数列.我们设这个等差数列的两端为[L,R].则如果区间[L+1,R-1]的格子被打实际上是不会影响L和R的(列一个等差数列实际模拟一下就发现啦).而如果格子L被打,则L+2:如果格子R被打,则R-2.打了格子后,别忘了L--,R++. 嗯根据以上性质,我们可以知道,地鼠1,3,5,7,9...的L是非递减的,地鼠2,4,6,8,10...的L也是非递减的. 然后看一

[2016北京集训测试赛15]statement-[线段树+拆环]

Description Solution 由于题目要求,将a[i]->b[i](边权为i)后所得的图应该是由森林和环套树组合而成. 假如是树形结构,所有的t[i]就直接在线段树t[i]点的dfs序(即in[t[i]],out[t[i]]区间)处记录t[i]点的深度. 这样,针对所有的f[i],在线段树上查找所有包含in[f[i]]点的区间所记录的最大深度d.(这个深度就是在离f[i]最近并且已经验证了是真命题的祖先的深度) 然后用倍增算出f[i]向上到深度d,所经过的编号最大值c.ans=min