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 #define M 1200000
  6 using namespace std;
  7
  8 int ans[M],to[M],head[M],next[M],vis[M],size[M],deep[M],id[M],top[M],sum[M],fa[M],a[M],cnt,dfn,n;
  9
 10 void add(int x,int y)
 11 {
 12     to[++cnt]=y;
 13     next[cnt]=head[x];
 14     head[x]=cnt;
 15 }
 16
 17 void dfs1(int x)
 18 {
 19     size[x]=vis[x]=1;
 20     for (int i=head[x];i;i=next[i])
 21         if (!vis[to[i]])
 22         {
 23             deep[to[i]]=deep[x]+1;
 24             fa[to[i]]=x;
 25             dfs1(to[i]);
 26             size[x]+=size[to[i]];
 27         }
 28 }
 29
 30 void dfs2(int x,int chain)
 31 {
 32     int k=0,i;
 33     id[x]=++dfn;
 34     top[x]=chain;
 35     for (i=head[x];i;i=next[i])
 36         if (deep[to[i]]>deep[x] && size[to[i]]>size[k]) k=to[i];
 37     if (!k) return;
 38     dfs2(k,chain);
 39     for (i=head[x];i;i=next[i])
 40         if (deep[to[i]]>deep[x] && to[i]!=k) dfs2(to[i],to[i]);
 41 }
 42
 43 void push_down(int cur)
 44 {
 45     sum[cur<<1]+=sum[cur];
 46     sum[cur<<1|1]+=sum[cur];
 47     sum[cur]=0;
 48 }
 49
 50 void ADD(int L,int R,int l,int r,int cur)
 51 {
 52     if (l<=L && r>=R)
 53     {
 54         sum[cur]++;
 55         return;
 56     }
 57     push_down(cur);
 58     int mid=L+R>>1;
 59     if (l>mid) ADD(mid+1,R,l,r,cur<<1|1);
 60     else if (r<=mid) ADD(L,mid,l,r,cur<<1);
 61          else ADD(L,mid,l,mid,cur<<1),ADD(mid+1,R,mid+1,r,cur<<1|1);
 62 }
 63
 64 int ask(int l,int r,int x,int cur)
 65 {
 66     if (l==r) return sum[cur];
 67     push_down(cur);
 68     int mid=l+r>>1;
 69     if (x>mid) return sum[cur]+ask(mid+1,r,x,cur<<1|1);
 70     else return sum[cur]+ask(l,mid,x,cur<<1);
 71 }
 72
 73 void Add(int x,int y)
 74 {
 75     for (;top[x]!=top[y];x=fa[top[x]])
 76     {
 77         if (deep[top[x]]<deep[top[y]]) swap(x,y);
 78         ADD(1,n,id[top[x]],id[x],1);
 79     }
 80     if (deep[x]<deep[y]) swap(x,y);
 81     ADD(1,n,id[y],id[x],1);
 82 }
 83
 84 int main()
 85 {
 86     int i,x,y;
 87     scanf("%d",&n);
 88     for (i=1;i<=n;i++) scanf("%d",&a[i]);
 89     for (i=1;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
 90     dfs1(1);
 91     dfs2(1,1);
 92     for (i=1;i<n;i++) Add(a[i],a[i+1]);
 93     for (i=1;i<=n;i++)
 94     {
 95         ans[i]=-(i!=a[1]);
 96         ans[i]+=ask(1,n,id[i],1);
 97     }
 98     for (i=1;i<=n;i++) printf("%d\n",ans[i]);
 99     return 0;
100 }
时间: 2024-08-27 12:52:54

BZOJ3631[JLOI2014]松鼠的新家 题解的相关文章

BZOJ3631: [JLOI2014]松鼠的新家

传送门 树上的差分优化,很简单的一道题,应该属于NOIP2015TGD2T3的子问题. //BZOJ 3631 //by Cydiater //2016.10.25 #include <iostream> #include <cstdlib> #include <cstdio> #include <cmath> #include <ctime> #include <cstring> #include <string> #i

BZOJ3631: [JLOI2014]松鼠的新家 树链剖分/lca/树上查分

求n次lca后树上差分. 每次移动时在起始点和终点各打一个start标记,在LCA和LCA的父节点处各上打一个end标记,然后深搜,start标记一直上传,遇到end标记就停止,最后再处理一下就行 % PoPoQQQ大爷 #include<bits/stdc++.h> #define rep(i,l,r) for(int i=l;i<=r;i++) #define N 310000 using namespace std; int head[N],dep[N],sz[N],son[N],

【树链剖分】【树状数组】【最近公共祖先】【块状树】bzoj3631 [JLOI2014]松鼠的新家

裸题,树状数组区间修改+单点查询.当然要稍微讨论一下链的左右端点是否修改的情况咯. #include<cstdio> #include<algorithm> #include<cmath> using namespace std; #define N 300001 int en,v[N<<1],first[N],next[N<<1],n; void AddEdge(const int &U,const int &V) { v[++

bzoj3631: [JLOI2014]松鼠的新家(LCA+差分)

题目大意:一棵树,以一定顺序走完n个点,求每个点经过多少遍 可以树链剖分,也可以直接在树上做差分序列的标记 后者打起来更舒适一点.. 具体实现: 先求x,y的lca,且dep[x]<dep[y], 如果在一棵子树下的一条链上,那么lca就是x 则g[fa[x]]--; g[y]++; 如果在一棵子树的两条分枝上,那么lca设为z g[x]++, g[y]++, g[z]--, g[fa[z]]-- 最后从叶子节点加回根节点,加法是差分序列的那种加法 因为z会加左右两边,多加了1,所以要减去. 1

[填坑]树上差分 例题:[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,

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,去参观新家.可是这样会导致维尼重复走很多房间,懒惰的维尼不停地推辞.可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃. 维尼是个馋家伙,立马就