题解 P3258 【[JLOI2014]松鼠的新家】

这道题可以说是树剖模板题。。。然而我一直WA10找不出错误。。。后来才发现是手抖打少了一个两个字符。。。



其实题目说的就是给你一颗树和一个遍历顺序,然后按照遍历顺序更新路径的值,最后查询所有节点的值。

其实这种题用树上差分会更理想,但是为了练树剖也就写了树剖。



AC代码如下:

1097ms 56696kb

#include<bits/stdc++.h>

using namespace std;

namespace StandardIO {

    template<typename T>inline void read (T &x) {
        x=0;T f=1;char c=getchar();
        for (; c<‘0‘||c>‘9‘; c=getchar()) if (c==‘-‘) f=-1;
        for (; c>=‘0‘&&c<=‘9‘; c=getchar()) x=x*10+c-‘0‘;
        x*=f;
    }

    template<typename T>inline void write (T x) {
        if (x<0) putchar(‘-‘),x*=-1;
        if (x>=10) write(x/10);
        putchar(x%10+‘0‘);
    }

}

using namespace StandardIO;

namespace Solve {

    const int N=300300;

    int n;
    int a[N];
    vector<int>graph[N];
    int index;
    int fa[N],dep[N],size[N],son[N],dfn[N],top[N];
    struct node {
        int l,r,val,tag;
    }tree[N<<2];

    void dfs1 (int now,int father) {
        fa[now]=father,dep[now]=dep[father]+1,size[now]=1;
        for (register int i=0; i<graph[now].size(); ++i) {
            int to=graph[now][i];
            if (to==father) continue;
            dfs1(to,now);
            size[now]+=size[to];
            if (size[to]>size[son[now]]) son[now]=to;
        }
    }
    void dfs2 (int now,int topf) {
        dfn[now]=++index,top[now]=topf;
        if (!son[now]) return;
        dfs2(son[now],topf);
        for (register int i=0 ;i<graph[now].size(); ++i) {
            int to=graph[now][i];
            if (to==fa[now]||to==son[now]) continue;
            dfs2(to,to);
        }
    }
    inline void pushup (int pos) {
        tree[pos].val=tree[pos<<1].val+tree[pos<<1|1].val;
    }
    inline void pushdown (int pos) {
        if (tree[pos].tag) {
            tree[pos<<1].tag+=tree[pos].tag,tree[pos<<1|1].tag+=tree[pos].tag;
            tree[pos<<1].val+=(tree[pos<<1].r-tree[pos<<1].l+1)*tree[pos].tag;
            tree[pos<<1|1].val+=(tree[pos<<1|1].r-tree[pos<<1|1].l+1)*tree[pos].tag;
            tree[pos].tag=0;
        }
    }
    void build (int l,int r,int pos) {
        tree[pos].l=l,tree[pos].r=r,tree[pos].val=tree[pos].tag=0;
        if (l==r) return;
        int mid=(l+r)>>1;
        build(l,mid,pos<<1),build(mid+1,r,pos<<1|1);
        pushup(pos);
    }
    void update (int l,int r,int v,int pos) {
        if (l<=tree[pos].l&&tree[pos].r<=r) {
            tree[pos].val+=v,tree[pos].tag+=v;
            return;
        }
        pushdown(pos);
        int mid=(tree[pos].l+tree[pos].r)>>1;
        if (l<=mid) update(l,r,v,pos<<1);
        if (mid<r) update(l,r,v,pos<<1|1);
        pushup(pos);
    }
    int query (int l,int r,int pos) {
        if (l<=tree[pos].l&&tree[pos].r<=r) {
            return tree[pos].val;
        }
        pushdown(pos);
        int mid=(tree[pos].l+tree[pos].r)>>1,ans=0;
        if (l<=mid) ans+=query(l,r,pos<<1);
        if (mid<r) ans+=query(l,r,pos<<1|1);
        return ans;
    }
    inline void updatePath (int x,int y,int v) {
        while (top[x]!=top[y]) {
            if (dep[top[x]]<dep[top[y]]) swap(x,y);
            update(dfn[top[x]],dfn[x],v,1);
            x=fa[top[x]];
        }
        if (dep[x]>dep[y]) swap(x,y);
        update(dfn[x],dfn[y],v,1);
    }
    inline int queryNode (int x) {
        return query(dfn[x],dfn[x],1);
    }

    inline void solve () {
        read(n);
        for (register int i=1; i<=n; ++i) {
            read(a[i]);
        }
        for (register int i=1; i<=n-1; ++i) {
            int x,y;
            read(x),read(y);
            graph[x].push_back(y);
            graph[y].push_back(x);
        }
        dfs1(1,0);
        dfs2(1,1);
        build(1,n,1);
        for (register int i=1; i<=n-1; ++i) {
            updatePath(a[i],a[i+1],1);
        }
        for (register int i=1; i<=n; ++i) {
            write((i==a[1])?queryNode(i):queryNode(i)-1),putchar(‘\n‘);
        }
    }
}

using namespace Solve;

int main () {
//    freopen(".in","r",stdin);
//    freopen(".out","w",stdout);
    solve();
}

原文地址:https://www.cnblogs.com/ilverene/p/9807568.html

时间: 2024-08-27 09:47:51

题解 P3258 【[JLOI2014]松鼠的新家】的相关文章

P3258 [JLOI2014]松鼠的新家

P3258 [JLOI2014]松鼠的新家倍增lca+树上差分,从叶子节点向根节点求前缀和,dfs求子树和即可,最后,把每次的起点和终点都. 1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #include<algorithm> 5 #include<cmath> 6 #include<ctime> 7 #include<set> 8 #include

【洛谷】【lca+树上差分】P3258 [JLOI2014]松鼠的新家

[题目描述:] 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n(2 ≤ n ≤ 300000)个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在”树“上. 松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,......,最后到an,去参观新家.可是这样会导致维尼重复走很多房间,懒惰的维尼不停地推辞.可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃. 维尼是个馋家伙,立马就

[Luogu] P3258 [JLOI2014]松鼠的新家

题目描述 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在”树“上. 松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,......,最后到an,去参观新家.可是这样会导致维尼重复走很多房间,懒惰的维尼不停地推辞.可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃. 维尼是个馋家伙,立马就答应了.现在松鼠希望知道为了保证维尼有

[JLOI2014]松鼠的新家 (树剖)

题目 P3258 [JLOI2014]松鼠的新家 解析 非常裸的一道树剖题 链上修改+单点查询的板子 记录一下所经过的点\(now[i]\),每次更新\(now[i-1]到now[i]\) 我们链上更新时上一次到的终点,是这一次一次更新的起点,又因为在\(a_n\)处可以不放糖,所以我们每次链上更新完成后,在这条链的终点位置处糖数\(-1\). 然后套板子直接做 代码 #include <bits/stdc++.h> using namespace std; const int N = 2e6

[填坑]树上差分 例题:[JLOI2014]松鼠的新家(LCA)

今天算是把LCA这个坑填上了一点点,又复习(其实是预习)了一下树上差分.其实普通的差分我还是会的,树上的嘛,也是懂原理的就是没怎么打过. 我们先来把树上差分能做到的看一下: 1.找所有路径公共覆盖的边 例题:[NOIP2015]运输计划 (然而我还没过就先不讲了) 反正就是中间有一步要求一条边被所有计划公共覆盖. 那么怎么求它呢?暴力(滚粗).我们有一个非常好的方法就是树上差分(记录tmp为差分数组) 询问操作为从叶子节点的权值向上累加到root 在一条路径u→ v,如果tmp[u]++,那么我

【BZOJ 3631】 [JLOI2014]松鼠的新家

3631: [JLOI2014]松鼠的新家 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 681 Solved: 329 [Submit][Status][Discuss] Description 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在"树"上.松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的

BZOJ 3631: [JLOI2014]松鼠的新家( 树链剖分 )

裸树链剖分... ------------------------------------------------------------------- #include<bits/stdc++.h> using namespace std; const int maxn = 300009; struct edge { int to; edge* next; } E[maxn << 1], *pit = E, *head[maxn]; inline void add(int u,

【Luogu P3258】[JLOI2014]松鼠的新家

Luogu P3258 题意就是对于一棵树,要求按照给出的顺序对每一个节点进行访问,记录每一个节点被经过的次数:特别地,我们认为只有从一个节点往外走才能被认为是经过一次.(最后一句话非常重要,仔细理解题意) 前置知识:树链剖分,差分. 最开始看到这道题我是打算使用树链剖分+线段树来做的. 但是我发现这个答案只需要每一个房间的糖果数--也就是说只需要区间修改+单点查询. 如果使用线段树的话,可能造成大量的空间浪费,而且常数也不小. 所以,我选择了使用树链剖分+差分进行统计. 由于差分写得比较少,本

BZOJ3631[JLOI2014]松鼠的新家 题解

题目大意: 给你一棵树,要从编号为a[1]的节点走到编号为a[2]的节点再走到编号为a[3]的节点……一直走到编号为a[n]的节点.问每个节点最少访问多少次. 思路: 将其进行轻重链剖分,则从a[i]走到a[i+1]实际上就是在几段重链的节点上+1,于是就用线段树来维护一下即可. 代码: 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #d