题目链接

题目大意:给定一颗含有$n$个结点的树,每个结点有一个权值$w$。给定$m$条路径,如果一个点与路径的起点的距离恰好为$w$,那么$ans[i]++$。求所有结点的ans。

题目分析

暴力的做法当然是枚举条路径,然后玄学$dfs$,复杂度应该是$O(nm)$的。再根据约束条件可以拿到65pts。

正解

对于一条路径$(u,v)$,我们可以将其分成两段:$(u,lca(u,v))$和$(lca(u,v),v)$。

我们先来分析上行路段。上行路段的要求有3个:

1.$u$在以$i$为根的子树里面。

2.$lca(u,v)$在以$i$为根的子树外面。

3.$dep[u]=dep[i]+w[i]$

同理对于下行路段也有3个条件:

1.$v$在以$i$为根的子树里面。

2.$lca(u,v)$在以i为根的子树外面。

3.$dis[s,t]-dep[t]=w[i]-dep[i]$

这样我们可以枚举每个结点,即dfs整棵树,复杂度$O(n)$。

对于这道题,我们还需要用桶来统计贡献。具体操作方法:

b1:上行阶段的贡献值。

b2:下行阶段的贡献值。

void dfs2(int x)
{
int t1=b1[w[x]+dep[x]], t2=b2[w[x]-dep[x]+maxn];//递归前的ans[x]
for(int i=head[x]; i; i=edge[i].next)
{
int y=edge[i].to;
if(y==fa[x][]) continue;
dfs2(y);//递归整棵树
}
b1[dep[x]]+=st[x];
for(int i=head1[x]; i; i=edge1[i].next)//h1是用链式前向星存的每个点作为终点的路径集合
{
int y=edge1[i].to;
b2[dis[y]-dep[t[y]]+maxn]++;//根据前面的等式。方法类似雨后的尾巴
}
ans[x]+=b1[w[x]+dep[x]]-t1+b2[w[x]-dep[x]+maxn]-t2;//加上差值
///////未完待续////////
}

我们不能忘记一点:树是递归进行操作的。

什么意思?还记得之前的约束条件吗?统计答案时$lca(u,v)$必然不能存在于子树中。所以当点i作为lca(u,v)时,统计完答案后要减去$(u,v)$对i的贡献。因为$(u,v)$的贡献对于i的祖先是不合法的。

for(int i=head2[x]; i; i=edge2[i].next)//h2是链式前向星存的每个点作为lca的路径集合
{
int y=edge2[i].to;
b1[dep[s[y]]]--;
b2[dis[y]-dep[t[y]]+maxn]--;
}

主函数主要代码:

for (int i=;i<=m;i++)
{
s[i]=read(),t[i]=read();
int ll=lca(s[i],t[i]);
dis[i]=dep[s[i]]+dep[t[i]]-*dep[ll];
st[s[i]]++;//统计以此点作为起点的路径条数
add1(t[i],i);//存
add2(ll,i);
if (dep[ll]+w[ll]==dep[s[i]]) ans[ll]--;//防止重复统计:当路径起点或终点恰好为两点LCA时且LCA处可以观察到运动员
}

注意数组下标的平移。时间复杂度$O(nlogn)$。

完整代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=;
int n,m;
int fa[maxn][],dep[maxn],b1[maxn*],b2[maxn*];
int dis[maxn],ans[maxn],s[maxn],t[maxn],st[maxn],w[maxn];
int head[maxn*],cnt,head1[maxn*],cnt1,head2[maxn*],cnt2;
struct node
{
int next,to;
}edge[maxn*],edge1[maxn*],edge2[maxn*];
inline int read()
{
int x=,f=;char ch=getchar();
while(!isdigit(ch)){if (ch=='-') f=-;ch=getchar();}
while(isdigit(ch)){x=x*+ch-'';ch=getchar();}
return x*f;
}
void add(int x, int y)
{
edge[++cnt].to=y;
edge[cnt].next=head[x];
head[x]=cnt;
}
void add1(int x, int y)
{
edge1[++cnt1].to=y;
edge1[cnt1].next=head1[x];
head1[x]=cnt1;
}
void add2(int x, int y)
{
edge2[++cnt2].to=y;
edge2[cnt2].next=head2[x];
head2[x]=cnt2;
}
inline void dfs1(int now)
{
for (int i=;(<<i)<=dep[now];i++)
fa[now][i]=fa[fa[now][i-]][i-];
for (int i=head[now];i;i=edge[i].next)
{
int to=edge[i].to;
if (to==fa[now][]) continue;
fa[to][]=now;
dep[to]=dep[now]+;
dfs1(to);
}
}
inline int lca(int x,int y)
{
if (x==y) return x;
if (dep[x]<dep[y]) swap(x,y);
int t=log(dep[x]-dep[y])/log();
for (int i=t;i>=;i--)
{
if (dep[fa[x][i]]>=dep[y])
x=fa[x][i];
if (x==y) return x;
}
t=log(dep[x])/log();
for (int i=t;i>=;i--)
{
if (fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
}
return fa[x][];
}
void dfs2(int x)
{
int t1=b1[w[x]+dep[x]], t2=b2[w[x]-dep[x]+maxn];
for(int i=head[x]; i; i=edge[i].next)
{
int y=edge[i].to;
if(y==fa[x][]) continue;
dfs2(y);
}
b1[dep[x]]+=st[x];
for(int i=head1[x]; i; i=edge1[i].next)
{
int y=edge1[i].to;
b2[dis[y]-dep[t[y]]+maxn]++;
}
ans[x]+=b1[w[x]+dep[x]]-t1+b2[w[x]-dep[x]+maxn]-t2;
for(int i=head2[x]; i; i=edge2[i].next)
{
int y=edge2[i].to;
b1[dep[s[y]]]--;
b2[dis[y]-dep[t[y]]+maxn]--;
}
} signed main()
{
n=read(),m=read();
for (int i=;i<n;i++)
{
int x=read(),y=read();
add(x,y);add(y,x);
}
dep[]=;fa[][]=;
dfs1();
for (int i=;i<=n;i++) w[i]=read();
for (int i=;i<=m;i++)
{
s[i]=read(),t[i]=read();
int ll=lca(s[i],t[i]);
dis[i]=dep[s[i]]+dep[t[i]]-*dep[ll];
st[s[i]]++;
add1(t[i],i);
add2(ll,i);
if (dep[ll]+w[ll]==dep[s[i]]) ans[ll]--;
}
dfs2();
for (int i=;i<=n;i++) printf("%lld ",ans[i]);
return ;
}

【NOIP2016】天天爱跑步 题解(LCA+桶+树上差分)的更多相关文章

  1. NOIP2016天天爱跑步 题解报告【lca+树上统计(桶)】

    题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 nn个 ...

  2. [NOIP2016]天天爱跑步 题解(树上差分) (码长短跑的快)

    Description 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要 玩家每天按时上线,完成打卡任务.这个游戏的地图 ...

  3. [NOIP2016]天天爱跑步-题解

    题面传送门 解答 设第\(j\)号玩家在\(V_j\)时刻出发. 弱化问题:如果树退化成了一条链.则在\(j\)处的观察员能观察到的\(i\)号玩家当且仅当 \[ i玩家经过j,且 \begin{ca ...

  4. NOIP2016(D1T2)天天爱跑步题解

    首先声明这不是一篇算法独特的题解,仍然是"LCA+桶+树上差分",但这篇题解是为了让很多很多看了很多题解仍然看不懂的朋友们看懂的,其中就包括我,我也在努力地把解题的"思维 ...

  5. [Noip2016]天天爱跑步 LCA+DFS

    [Noip2016]天天爱跑步 Description 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.?天天爱跑步?是一个养成类游戏,需要玩家每天按时上线,完成打卡任 ...

  6. 「NOIP2016」天天爱跑步 题解

    (声明:图片来源于网络) 「NOIP2016」天天爱跑步 题解 题目TP门 题目 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是 ...

  7. Noip 2016 天天爱跑步 题解

    [NOIP2016]天天爱跑步 时间限制:2 s   内存限制:512 MB [题目描述] 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是 ...

  8. [NOIp2016]天天爱跑步 线段树合并

    [NOIp2016]天天爱跑步 LG传送门 作为一道被毒瘤出题人们玩坏了的NOIp经典题,我们先不看毒瘤的"动态爱跑步"和"天天爱仙人掌",回归一下本来的味道. ...

  9. 【LG1600】[NOIP2016]天天爱跑步

    [LG1600][NOIP2016]天天爱跑步 题面 洛谷 题解 考虑一条路径\(S\rightarrow T\)是如何给一个观测点\(x\)造成贡献的, 一种是从\(x\)的子树内出来,另外一种是从 ...

随机推荐

  1. JavaScript location对象、Navigator对象、Screen对象简介

    Location对象 location用于获取或设置窗体的URL,并且可以用于解析URL. 语法: location.[属性|方法] Location对象属性 Location对象方法: Naviga ...

  2. SQL字符串拼接FOR XML PATH

    在工作中难免会遇到数据库中数据要进行拼接的问题,字符串拼接可以是用SQL的拼接也可以使用C#的拼接,本次说的是使用SQL进行拼接. 首先插入测试语句: --测试语句,准备创建表的语句:如下 CREAT ...

  3. shell专题(七):流程控制(重点)

    7.1 if 判断 1.基本语法 if [ 条件判断式 ];then 程序 fi 或者 if [ 条件判断式 ] then 程序 fi 注意事项: (1)[ 条件判断式 ],中括号和条件判断式之间必须 ...

  4. MYSQL 之 JDBC(六): 增删改查(四)利用反射及JDBC元数据编写通用的查询

    1.先利用SQL进行查询,得到结果集2.利用反射创建实体类的对象:创建Student对象3.获取结果集的列的别名:idCard.studentName4.再获取结果集的每一列的值,结合3得到一个Map ...

  5. 使用Vue做出跑马灯效果

     <div id="pmd">         <h4> {{msg}}</h4>         <input type="b ...

  6. Vmware虚拟机下不能访问网络的解决办法之一

    Vmware虚拟机下不能访问网络的解决办法之一 1.这个是默认的网络设置 2.如果不能访问网络,看下VMware相关的服务有没有打开,win+R 3.找到VMware的相关选项,全部启用(当然网络可能 ...

  7. Jmeter系列(45)- 详解 Jmeter 跨线程组取参数值的方法,免代码!

    如果你想从头学习Jmeter,可以看看这个系列的文章哦 https://www.cnblogs.com/poloyy/category/1746599.html 前言 用过 Jmeter 的同学应该都 ...

  8. Oracle-常见的命令

    --------------输入一下指令,按下快捷键 F8 select * from emp; --------------创建表格 create table 表名( 字段名1 数据类型1, 字段名 ...

  9. STL源码剖析:关联式容器

    AVL树 AVL树定义:红黑树是一颗二叉搜索树,特别的是一棵保持高度平衡的二叉搜索树 AVL树特点: 每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1 AVL树插入: 说明:新增节点的平衡因子 ...

  10. 题解 洛谷 P3298 【[SDOI2013]泉】

    考虑到年份数很小,只有 \(6\),所以可以 \(2^6\) 来枚举子集,确定流量指数对应相同的位置,然后通过哈希和排序来计算相同的方案数. 但是这样计算出的是大于等于子集元素个数的方案数,所以还需要 ...