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 #include<stdio.h>
 2 #include<algorithm>
 3 using namespace std;
 4 const int maxn = 300010;
 5 struct node{
 6     int to,next;
 7 }e[maxn*2];
 8 int n,a[maxn],head[maxn],dep[maxn],fa[maxn][20],w[maxn],f[maxn],tot,logn;
 9
10 void insert(int u, int v){
11     e[++tot].to=v; e[tot].next=head[u]; head[u]=tot;
12 }
13
14 void dfs(int u, int f, int d){
15     dep[u]=d; fa[u][0]=f;
16     for (int i=1; i<=logn; i++) fa[u][i]=fa[fa[u][i-1]][i-1];
17     for (int i=head[u]; i; i=e[i].next)
18         if (e[i].to!=f) dfs(e[i].to,u,d+1);
19 }
20
21 void lca(int u, int v){
22     if (dep[u]<dep[v]) swap(u,v);
23     int x=u,y=v;
24     while (dep[u]>dep[v]){
25         for (int i=logn; i>=0; i--)
26             if (dep[fa[u][i]]>dep[v])
27                 u=fa[u][i];
28         u=fa[u][0];
29     }
30     if (u==v){  //在某条链上
31         w[fa[u][0]]--;
32         w[x]++;
33         return;
34     }
35     for (int i=logn; i>=0; i--)  //在分叉口上
36         if (fa[u][i]!=fa[v][i]){
37             u=fa[u][i];
38             v=fa[v][i];
39         }
40     u=fa[u][0];
41     w[x]++; w[y]++;
42     w[u]--; w[fa[u][0]]--;
43     return;
44 }
45
46 void get_ans(int u){
47     f[u]=w[u];// printf("  %d\n", w[u]);
48     for (int i=head[u],v; i; i=e[i].next){
49         if ((v=e[i].to)==fa[u][0]) continue;
50         get_ans(e[i].to);
51         f[u]+=f[v];
52     }
53 }
54
55 int main(){
56     scanf("%d", &n);
57     for (int i=1; i<=n; i++) scanf("%d", &a[i]); tot=1; while ((1<<logn)<n) logn++;
58     for (int i=1,u,v; i<n; i++) scanf("%d%d", &u, &v),insert(u,v),insert(v,u); insert(0,a[1]);
59     dfs(0,0,1);
60     for (int i=2; i<=n; i++){
61         lca(a[i-1],a[i]);
62     }
63     get_ans(a[1]);
64     for (int i=1; i<=n; i++) printf("%d\n", (i==a[1])?f[i]:f[i]-1);
65     return 0;
66 }
时间: 2024-08-17 05:47:48

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

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],

BZOJ 3631 [JLOI2014]松鼠的新家 | 树上差分

链接 BZOJ 3631 题解 看起来是树剖?实际上树上差分就可以解决-- 当要给一条路径(u, v) +1的时候,给d[u] += 1, d[v] += 1, d[lca(u, v)] -= 1, d[fa[lca(u, v)]] -= 1. 注意这道题中路径的终点是不 +1的. #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <q

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]松鼠的新家

裸题,树状数组区间修改+单点查询.当然要稍微讨论一下链的左右端点是否修改的情况咯. #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]松鼠的新家 题解

题目大意: 给你一棵树,要从编号为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

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

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

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

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

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

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

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