题面

题目链接

P3258 [JLOI2014]松鼠的新家

题目描述

松鼠的新家是一棵树,前几天刚刚装修了新家,新家有 $ n $ 个房间,并且有 $ n-1 $ 根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在”树“上。

松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去 $ a_1 $,再去 $ a_2 $,......,最后到 $ a_n $,去参观新家。可是这样会导致维尼重复走很多房间,懒惰的维尼不停地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。

维尼是个馋家伙,立马就答应了。现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。

因为松鼠参观指南上的最后一个房间 $ a_n $ 是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。

输入输出格式

输入格式

第一行一个整数 $ n $,表示房间个数第二行n个整数,依次描述 $ a_1-a_n $

接下来 $ n-1 $ 行,每行两个整数 $ x $ , $ y $ ,表示标号x和y的两个房间之间有树枝相连。

输出格式

一共 $ n $ 行,第 $ i $ 行输出标号为i的房间至少需要放多少个糖果,才能让维尼有糖果吃。

输入输出样例

输入样例:

5
1 4 5 3 2
1 2
2 4
2 3
4 5

输出样例:

1
2
1
2
1

说明

【数据范围】

$ 2 \leq n \leq 300000 $

说明

【时空限制】

1000ms,128M

思路

整理题意,可参考如下理解:

给定一个n棵结点的树,一个人从给定的起点出发按一定的顺序走至终点;他经过每个点时,该点点权加1 (包括起点但不包括终点)。求最终每点的点权。

首先我考虑的是每次改变a[i]和a[i+1]之间所有点的点权。可以拿纸模拟一下,会发现这样的问题。

这样计算,那么样例得到的结果最终为1 3 2 3 2

由于在a[i]到a[i+1]再到a[i+2]的过程中,按照上面的算法,先改变a[i]和a[i+1]之间所有点的点权,再改变a[i+1]和a[i+2]之间所有点的点权,那么a[i+1]的点权就重复算了一次。故除了起点和终点,其他每个点点权都多算了1,应该减去;而终点也多算了一次,原因是最后一次到达终点时点权不用加1;起点并没有重复,不用减去。

所以最终算法归纳为:

1.每次改变a[i]和a[i+1]之间所有点的点权,$ 1 \leq i < n $

2.将除起点外的所有点点权减1

这样基本上是一道树剖的板子题了吧

AC代码

#include<bits/stdc++.h>
const int maxn=300010;
using namespace std; int n,a[maxn];
int tot,to[maxn<<1],nxt[maxn<<1],head[maxn];
int dep[maxn],fa[maxn],son[maxn],len[maxn];
int cnt,nid[maxn],nw[maxn],top[maxn];
struct SegmentTree
{
int l,r,tag,sum;
#define l(a) tree[a].l
#define r(a) tree[a].r
#define m(a) ((l(a)+r(a))>>1)
#define len(a) (r(a)-l(a)+1)
#define t(a) tree[a].tag
#define s(a) tree[a].sum
}tree[maxn<<2]; void dfs1(int u,int f,int d)
{
dep[u]=d;fa[u]=f;len[u]=1;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==f) continue;
dfs1(v,u,d+1);
len[u]+=len[v];
if(len[v]>len[son[u]]) son[u]=v;
}
} void dfs2(int p,int t)
{
nid[p]=++cnt;
top[p]=t;
if(!son[p]) return;
dfs2(son[p],t);
for(int i=head[p];i;i=nxt[i])
{
int v=to[i];
if(v==fa[p] || v==son[p]) continue;
dfs2(v,v);
}
} void BuildTree(int p,int l,int r)
{
l(p)=l;r(p)=r;
if(l==r) return;
BuildTree(p<<1,l,m(p));
BuildTree(p<<1|1,m(p)+1,r);
} void PushDown(int p)
{
if(t(p))
{
s(p<<1)+=t(p)*len(p<<1);
s(p<<1|1)+=t(p)*len(p<<1|1);
t(p<<1)+=t(p);
t(p<<1|1)+=t(p);
t(p)=0;
}
} void Change1(int p,int l,int r)
{
if(l<=l(p) && r>=r(p))
{
t(p)++;
s(p)+=len(p);
return;
}
PushDown(p);
if(l<=m(p)) Change1(p<<1,l,r);
if(r>m(p)) Change1(p<<1|1,l,r);
s(p)=s(p<<1)+s(p<<1|1);
} int Ask(int p,int l,int r)
{
if(l<=l(p) && r>=r(p)) return s(p);
PushDown(p);
int ans=0;
if(l<=m(p)) ans+=Ask(p<<1,l,r);
if(r>m(p)) ans+=Ask(p<<1|1,l,r);
return ans;
} void Change2(int u,int v)
{
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
Change1(1,nid[top[u]],nid[u]);
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
Change1(1,nid[u],nid[v]);
} int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<n;i++)
{
int u,v;scanf("%d%d",&u,&v);
to[++tot]=v;nxt[tot]=head[u];head[u]=tot;
to[++tot]=u;nxt[tot]=head[v];head[v]=tot;
}
dfs1(a[1],a[1],1);
dfs2(a[1],a[1]);
BuildTree(1,1,n);
for(int i=1;i<n;i++) Change2(a[i],a[i+1]);
for(int i=1;i<=n;i++) printf("%d\n",Ask(1,nid[i],nid[i])-(i==a[1]? 0:1));
return 0;
}

优化

然而,这个题的修改操作很简单,就是很多个区间上同时加1;而如果用线段树,还会存储一堆区间和,有点浪费。可以考虑差分前缀和的方式

如果b是a的差分数组,那么在a的某个[i,j]内同时加上1,等效于b[i]++,b[j+1]--;统计答案时只需要从左到右扫一遍就可以了

优化后AC代码

#include<bits/stdc++.h>
const int maxn=300010;
using namespace std; int n,a[maxn];
int tot,to[maxn<<1],nxt[maxn<<1],head[maxn];
int dep[maxn],fa[maxn],son[maxn],len[maxn];
int cnt,nid[maxn],top[maxn];
int ans[maxn];
struct SegmentTree
{
int l,r,tag,sum;
#define l(a) tree[a].l
#define r(a) tree[a].r
#define m(a) ((l(a)+r(a))>>1)
#define len(a) (r(a)-l(a)+1)
#define t(a) tree[a].tag
#define s(a) tree[a].sum
}tree[maxn<<2]; void dfs1(int u,int f,int d)
{
dep[u]=d;fa[u]=f;len[u]=1;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==f) continue;
dfs1(v,u,d+1);
len[u]+=len[v];
if(len[v]>len[son[u]]) son[u]=v;
}
} void dfs2(int p,int t)
{
nid[p]=++cnt;
top[p]=t;
if(!son[p]) return;
dfs2(son[p],t);
for(int i=head[p];i;i=nxt[i])
{
int v=to[i];
if(v==fa[p] || v==son[p]) continue;
dfs2(v,v);
}
} void Change1(int i,int j)
{
ans[i]++;ans[j+1]--;
} void Change2(int u,int v)
{
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
Change1(nid[top[u]],nid[u]);
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
Change1(nid[u],nid[v]);
} int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<n;i++)
{
int u,v;scanf("%d%d",&u,&v);
to[++tot]=v;nxt[tot]=head[u];head[u]=tot;
to[++tot]=u;nxt[tot]=head[v];head[v]=tot;
}
dfs1(a[1],a[1],1);
dfs2(a[1],a[1]);
for(int i=1;i<n;i++) Change2(a[i],a[i+1]);
for(int i=2;i<=n;i++) ans[i]+=ans[i-1];
for(int i=1;i<=n;i++) printf("%d\n",ans[nid[i]]-(i==a[1]? 0:1)); ///判断是否是起点,如果不是起点则-1
return 0;
}

总结

区间加1可以用差分,这样仅仅只用更改两个端点

洛谷 P3258 [JLOI2014]松鼠的新家 树链剖分+差分前缀和优化的更多相关文章

  1. 洛谷 P3258 [JLOI2014]松鼠的新家(树链剖分)

    题目描述松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在”树“上. 松鼠想邀请小熊维尼前来 ...

  2. P3258 [JLOI2014]松鼠的新家 树链剖分

    这个题就是一道树剖板子题,就是每走一步就把所有的经过点加一就行了.还有,我的树剖板子没问题!!!谁知道为什么板子T3个点!我不管了!反正这道题正常写A了. 题干: 题目描述 松鼠的新家是一棵树,前几天 ...

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

    简要题意 树上n个节点,给定路径,求每个点经过次数 题意分析 对于每两个点,有两种情况,第一种,他们的lca为本身,第二种,他们有公共祖先,又要求他们的点经过次数,暴力是不可能的,复杂度不对,所以可以 ...

  4. 洛谷 P3258 [JLOI2014]松鼠的新家 解题报告

    P3258 [JLOI2014]松鼠的新家 题目描述 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他 ...

  5. 洛谷P3258 [JLOI2014]松鼠的新家

    P3258 [JLOI2014]松鼠的新家 题目描述 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他 ...

  6. 洛谷 P3258 [JLOI2014]松鼠的新家 题解

    P3258 [JLOI2014]松鼠的新家 题目描述 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他 ...

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

    裸树链剖分... ------------------------------------------------------------------- #include<bits/stdc++ ...

  8. Bzoj 3631: [JLOI2014]松鼠的新家(树链剖分+线段树)

    3631: [JLOI2014]松鼠的新家 Time Limit: 10 Sec Memory Limit: 128 MB Description 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个 ...

  9. 洛谷P3258 [JLOI2014]松鼠的新家(树上差分+树剖)

    题目描述 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在”树“上. 松鼠想邀请小熊维尼前 ...

随机推荐

  1. 转:linux进程间通信的几种机制的比较及适用场合

    源地址:http://blog.csdn.net/f_x_p0324/article/details/6878081 socket 1. # 管道( pipe ):管道是一种半双工的通信方式,数据只能 ...

  2. jquery与js区别

    js与jquery的区别 js里面找元素是通过dom操作,jquery是通过$ DOM:土鳖jQuery:土豪1. DOM-->jQuery(土鳖变土豪)拿钱砸:$Var txtName = d ...

  3. sort方法

    作用:对列表进行排序 >>> spam=[2,5,3,14,1,-7] >>> spam.sort() >>> spam [-7, 1, 2, 3 ...

  4. Eureka 客户端连接Eureka服务端时 报Cannot execute request on any known server 解决办法

    报Cannot execute request on any known server 这个错,总的来说就是连接Eureka服务端地址不对. 因为配置eureka.client.serviceUrl. ...

  5. Hadoop集群中有哪些节点类型

  6. GitBook的使用方法

    ---恢复内容开始--- 由于近期工作中使用gitbook编写讲义,现把出现的问题总结下: 1 . gitbook的安装 Gitbook与word等办公软件类似,能够编写文档,Gitbook中编写文档 ...

  7. Python的Django REST框架中的序列化及请求和返回

    Python的Django REST框架中的序列化及请求和返回 序列化Serialization 1. 设置一个新的环境 在我们开始之前, 我们首先使用virtualenv要创建一个新的虚拟环境,以使 ...

  8. MySQL数据库起步 关于数据库的基本操作(更新中...)

    mysql的基本操作 连接指定的服务器(需要服务器开启3306端口) mysql -h ip地址 -P 端口号 -u 账号 -p 密码 删除游客模式 mysql -h ip地址 -P 端口号 -u 账 ...

  9. git diff 笔记

    有一个 lab1 一个lab2 lab2 是比lab1 新的版本 但是Lab1 中有一些写的新代码,想要保留到lab2 中 直接使用patch 会把 lab2 回退到lab1 或lab1 更新到lab ...

  10. vue 报错解决:TypeError: Cannot read property '_t' of undefined"

    前端报错如下: [Vue warn]: Error in render: "TypeError: Cannot read property '_t' of undefined" 是 ...