树的dfs序,p1539,其他经典问题,2018/11/08模拟赛T3

    树的dfs序指从根节点进行dfs(先序遍历),每次到达某个点的时间和离开这个点的时间.它可以将树上的问题转换成序列问题进行处理.

  比如对于p1539的样例可以这样解释.

  

  每个点的左边数字表示进入该点的"时间",右边的数字表示离开该点的"时间".对dfs序的介绍就到这里.

  然后来看一个例题:

  

  

  先读入边,跑一遍dfs确定dfs序.

  对于操作1,把点x的进入的"时间"+=a,把x出去的"时间"-=a

  这样操作3询问根节点到y的路径点权和时,我们询问y的进入的时间的前缀和.如果这个路径经过点x,即x是y的祖先,由于x的进入的时间早于y进入的时间,x出去的时间晚于y进入的时间,贡献会正确的加上去,否则1.如果dfs序本来就在y之后当然不会影响.2.如果x的dfs序在y之前,一定进入的时间和出去的时间都早于y进入的时间,对y没有贡献.

  如果只有操作1,3,只需要一个树状数组即可.那来看一下操作2.将x的子树所有节点点权都+v,这个就有些难了吧.

  考虑区间修改,用一个线段树维护区间点权和与区间内"进入的数量-出去的数量",每次修改的时候将x进入的时间和出去的时间这个区间打上标记v.下传标记的时候可以使得区间和+=v*"进去的数量-出去的数量".

  至此,本题已经A掉了.很久没写区间修改的线段树了,竟然不知道区间修改需要懒标记...写代码20分钟,找错误30分钟,发现懒标记的问题后5分钟修改完毕.

   

  并且提交的时候出现了上述四种情况:数组开小(dfs序需要2n,那么线段树需要8n),没关freopen,没开long long,A掉.真是菜的真实.

long long i,tx,ty,t[100010],tv,tl,tr,flag,ttt,T,jia[800010];
long long n,m,head[100010],tot;
struct edge
{
    long long x,y,next;
}e[200010];
struct node
{
    long long l,r;
}o[100010];
long long c[800010],sum[800010];
void bian(long long x,long long y)
{
    tot++;
    e[tot].x=x;
    e[tot].y=y;
    e[tot].next=head[x];
    head[x]=tot;
}
void dfs(long long x)
{
    tot++;
    o[x].l=tot;
    for(long long j=head[x];j;j=e[j].next)
    {
        if(!o[e[j].y].l)
        {
            dfs(e[j].y);
        }
    }
    tot++;
    o[x].r=tot;
}
void build(long long now,long long l,long long r)
{
    if(flag)
        sum[now]++;
    else
        sum[now]--;
    if(l==r)
        return;
    long long mid=(l+r)>>1;
    if(tx<=mid)build(now<<1,l,mid);
    else       build(now<<1|1,mid+1,r);
}

void pushdown(long long now)
{
    jia[now<<1]+=jia[now];
    jia[now<<1|1]+=jia[now];
    c[now<<1]+=sum[now<<1]*jia[now];
    c[now<<1|1]+=sum[now<<1|1]*jia[now];
    jia[now]=0;
}
void add(long long now,long long l,long long r)
{
    c[now]+=tv;
    if(l==r)
        return ;
    if(jia[now])
        pushdown(now);
    long long mid=(l+r)>>1;
    if(ttt<=mid)add(now<<1,l,mid);
    else       add(now<<1|1,mid+1,r);
}
void qujian(long long now,long long l,long long r)
{
    if(tl<=l&&r<=tr)
    {
        c[now]+=sum[now]*tv;
        jia[now]+=tv;
        return ;
    }
    long long mid=(l+r)/2;
    if(jia[now])
        pushdown(now);
    if(tl<=mid)qujian(now<<1,l,mid);
    if(tr>mid)qujian(now<<1|1,mid+1,r);
    c[now]=c[now<<1]+c[now<<1|1];
}
long long ask(long long now,long long l,long long r)
{
    if(1<=l&&r<=tx)
        return c[now];
    long long mid=(l+r)/2;
    long long tsum=0;
    if(jia[now])
        pushdown(now);
    if(1<=mid)tsum+=ask(now<<1,l,mid);
    if(tx>mid)tsum+=ask(now<<1|1,mid+1,r);
    return tsum;
}
void check(long long now,long long l,long long r)
{
    cout<<l<<‘ ‘<<r<<‘ ‘<<sum[now]<<‘ ‘<<c[now]<<endl;
    if(l==r)
        return ;
    check(now<<1,l,(l+r)/2);
    check(now<<1|1,(l+r)/2+1,r);
}
int main()
{

int __size__ = 20 << 20; // 20MB
char *__p__ = (char*)malloc(__size__) + __size__;
__asm__("movl %0, %%esp\n" :: "r"(__p__));
    n=read();m=read();
    for(i=1;i<=n;i++)
        t[i]=read();
    for(i=1;i<n;i++)
    {
        tx=read();ty=read();
        bian(tx,ty);
        bian(ty,tx);
    }
    tot=0;
    dfs(1);
    for(i=1;i<=n;i++)
    {
        tx=o[i].l;
        flag=1;
        build(1,1,tot);
        tx=o[i].r;
        flag=0;
        build(1,1,tot);
        tv=t[i];
        ttt=o[i].l;
        add(1,1,tot);
        ttt=o[i].r;tv=-t[i];
        add(1,1,tot);
    }
    for(i=1;i<=m;i++)
    {
        T=read();
        if(T==1)
        {
            tx=read();tv=read();
            ttt=o[tx].l;
            add(1,1,tot);
            ttt=o[tx].r;tv=-tv;
            add(1,1,tot);
        }
        else if(T==2)
        {
            tx=read();tv=read();
            tl=o[tx].l;tr=o[tx].r;
            qujian(1,1,tot);
        }
        else //if(t==3)
        {
            tx=o[read()].l;
            write(ask(1,1,tot));
            putchar(10);
        }
    }
    //check(1,1,tot);
}

随便写个什么题都200行代码啊

  然后还有一些经典操作,这里来说一下.//以下认为你已经看过我的代码了.

  操作4,询问x到y的树链的点权和.这个是操作3的升级版,相当于询问区间[o[x].l,o[y].r]

原文地址:https://www.cnblogs.com/qywyt/p/10425330.html

时间: 2024-11-08 16:22:17

树的dfs序,p1539,其他经典问题,2018/11/08模拟赛T3的相关文章

dfs序七个经典问题[转]

dfs序七个经典问题 参考自:<数据结构漫谈>-许昊然 dfs序是树在dfs先序遍历时的序列,将树形结构转化成序列问题处理. dfs有一个很好的性质:一棵子树所在的位置处于一个连续区间中. ps:deep[x]为x的深度,l[x]为dfs序中x的位置,r[x]为dfs序中x子树的结束位置 1.点修改,子树和查询 在dfs序中,子树处于一个连续区间中.所以这题可以转化为:点修改,区间查询.用树状数组或线段树即可. 2.树链修改,单点查询 将一条树链x,y上的所有点的权值加v.这个问题可以等价为:

BZOJ 3779 重组病毒 LCT+线段树维护DFS序

题目大意:给定一棵树,初始每个点都有一个颜色,支持三种操作: 1.将某个点到根的路径上所有点染上一种新的颜色 2.将某个点到根的路径上所有点染上一种新的颜色,然后把根设为这个点 3.定义一个点的代价为这个点到根路径上颜色的种类数,求某个点子树中所有点代价的平均值 我真是炖了狗了-- 容易发现这玩应就是个LCT,操作1就是Access,操作2就是Move_To_Root,代价就是一个点到根路径上的虚边数量+1 我们用LCT模拟上述操作,用线段树维护DFS序维护信息,一旦LCT中出现了虚实边的切换,

CF877E Danil and a Part-time Job 线段树维护dfs序

\(\color{#0066ff}{题目描述}\) 有一棵 n 个点的树,根结点为 1 号点,每个点的权值都是 1 或 0 共有 m 次操作,操作分为两种 get 询问一个点 x 的子树里有多少个 1 pow 将一个点 x 的子树中所有节点取反 对于每个 get 给出答案 \(\color{#0066ff}{输入格式}\) 第一行一个整数 n 第二行共 n?1 个整数,第 i 个数 \(x_i\) 表示 \(x_i\) 是 i+1 的父亲, 第三行给出每个点的初始权值 第四行一个整数 m 接下来

HDU4117 GRE WORDS(AC自动机+线段树维护fail树的dfs序)

Recently George is preparing for the Graduate Record Examinations (GRE for short). Obviously the most important thing is reciting the words. Now George is working on a word list containing N words. He has so poor a memory that it is too hard for him

dfs序七个经典问题

参考自:<数据结构漫谈>-许昊然 dfs序是树在dfs先序遍历时的序列,将树形结构转化成序列问题处理. dfs有一个很好的性质:一棵子树所在的位置处于一个连续区间中. ps:deep[x]为x的深度,l[x]为dfs序中x的位置,r[x]为dfs序中x子树的结束位置 1.点修改,子树和查询 在dfs序中,子树处于一个连续区间中.所以这题可以转化为:点修改,区间查询.用树状数组或线段树即可. 2.树链修改,单点查询 将一条树链x,y上的所有点的权值加v.这个问题可以等价为: 1).x到根节点的链

【BZOJ2286】消耗战(虚树,DFS序,树形DP)

题意:一棵N个点的树上有若干个关键点,每条边有一个边权,现在要将这些关键点到1的路径全部切断,切断一条边的代价就是边权. 共有M组询问,每组询问有k[i]个关键点,对于每组询问求出完成任务的最小代价. 对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1 思路:第一题虚树,需要详细地记录一下. 对于此题,朴素的树形DP很好理解: dp[u]为将u子树中的关键点全部切断的最小代价 dp[u]=min(cut[u]

[BZOJ 3306]树(dfs序+线段树+倍增)

Description 给定一棵大小为 n 的有根点权树,支持以下操作: • 换根 • 修改点权 • 查询子树最小值 Solution 单点修改子树查询的话可以想到用dfs序+线段树来处理,换根的处理画一画图应该可以明白: 如果查询的x是当前的根rt,直接返回整棵树的min 如果rt在x的子树中,用倍增的方法找到离x最近的rt的祖先t,整棵树除t的子树以外的部分就是x当前根下的子树 如果rt不在x的子树中,查询x原来的子树的min值 #include<iostream> #include<

【Tyvj2133 BZOJ1146】网络管理Network(树套树,DFS序,树状数组,主席树,树上差分)

题意:有一棵N个点的树,每个点有一个点权a[i],要求在线实现以下操作: 1:将X号点的点权修改为Y 2:查询X到Y的路径上第K大的点权 n,q<=80000 a[i]<=10^8 思路:此题明显地体现了我对主席树理解不深 树上路径K大可以直接用树剖+二分答案+树做 但DFS序+主席树也可以 对于点U,它能影响DFS序上的区间(st[u],ed[u]) 所以维护方法就是类似序列K大一样 s[st[u]]++ s[ed[u]+1]-- 对于路径(x,y),信息为s[x]+s[y]-s[lca(x

HDU 5296 Annoying problem(LCA模板+树的dfs序心得)

Problem Description Coco has a tree, whose nodes are conveniently labeled by 1,2,-,n, which has n-1 edge,each edge has a weight. An existing set S is initially empty. Now there are two kinds of operation: 1 x: If the node x is not in the set S, add n