算法:LCA,树上差分+(乱搞)

如果有写错的地方请大佬更正

对于100%数据:

u表示起点,v表示终点

对于一条u到v的路径,先讨论LCA!=u&&LCA!=v的情况:

分为u到LCA的路径和LCA到v的路径

对于u到LCA的路径上的点x,当deep[u]-deep[x]=w[x]时,即w[x]+deep[x]=deep[u]时,这条路径对点x有贡献;

观察发现w[x]+deep[x]是定值,所以统计经过x的路径中,deep[u]=w[x]+deep[x]的路径条数。

对于LCA到v的路径上的点x,当deep[u]-2*deep[LCA]+deep[x]=w[x]时,即w[x]-deep[x]=deep[u]-2*deep[lca]时,这条路径对点x有贡献;

观察发现w[x]-deep[x]是定值,所以统计经过x的路径中,deep[u]-2*deep[lca]=w[x]-deep[x]的路径条数;

接下来就是统计路径条数了,用到树上差分

我们统计的起点(终点)一定在点x子树内,所以统计x子树内有多少起点(终点)的值等于所需值

即统计有多少个在点x子树内的起点的deep[u]的值与deep[x]+w[x]相同

有多少终点的deep[u]-2*deep[lca]与w[x]-deep[x]相同

对于一个值,再u、v上加一个表示这个值+1的标记

考虑到x子树内的路径不一定经过x,所以在father[LCA]上加一个标记表示这个值-1

标记用动态数组储存

然后一遍dfs用两个桶分别统计,统计时值统一加上n,因为可能出现负数

记录下dfs到父亲节点时自己(也就是父亲的儿子)所需值的个数,然后统计完子树的值之后再做差计算自己

对于LCA==u||LCA==v的情况归于以上两类计算,特殊处理一下

另外,对于分裂成两条链LCA可能会被统计两遍,最后特殊判断一下,如果被统计了两遍就减去一遍,

复杂度:

LCA O(mlogn)

dfs统计 O(n)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 300009
using namespace std;
int n,m;
vector<int>G[N];
int W[N];
int S[N],T[N],LCA[N]; int father[N],son[N],depth[N];
int heavyson[N],top[N];
int dfs1(int now,int fa){
father[now]=fa;
son[now]=1;
depth[now]=depth[fa]+1;
for(int i=0;i<G[now].size();++i){
if(G[now][i]!=fa){
dfs1(G[now][i],now);
son[now]+=son[G[now][i]];
if(son[G[now][i]]>son[heavyson[now]])heavyson[now]=G[now][i];
}
}
} int dfs2(int now,int first){
top[now]=first;
if(!heavyson[now])return 0;
dfs2(heavyson[now],first);
for(int i=0;i<G[now].size();++i){
if(G[now][i]!=father[now]&&G[now][i]!=heavyson[now])dfs2(G[now][i],G[now][i]);
}
} int swap(int &a,int &b){
int t=a;a=b;b=t;
} int lca(int u,int v){
int tu=top[u],tv=top[v];
while(tu!=tv){
if(depth[tu]<depth[tv]){
swap(tu,tv);swap(u,v);
}
u=father[tu];tu=top[u];
}
if(depth[u]<depth[v])return u;
else return v;
} int cnt[N];
int T1[N+N],T2[N+N];
struct tag{
int v,siz;
};
vector<tag>tag1[N];
vector<tag>tag2[N];
int dfs(int now,int a,int b){
for(int i=0;i<tag1[now].size();++i){
T1[tag1[now][i].v+N]+=tag1[now][i].siz;
}
for(int i=0;i<tag2[now].size();++i){
T2[tag2[now][i].v+N]+=tag2[now][i].siz;
} for(int i=0;i<G[now].size();++i){
int v=G[now][i];
if(v==father[now])continue;
dfs(v,T1[W[v]+depth[v]+N],T2[W[v]-depth[v]+N]);
} cnt[now]+=T1[W[now]+depth[now]+N]+T2[W[now]-depth[now]+N]-a-b;
} int read(){
int r=0,k=1;
char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-')k=-1;
for(;c>='0'&&c<='9';c=getchar())r=r*10+c-'0';
return r*k;
} int main(){
n=read();m=read();
for(int i=1;i<=n-1;++i){
int x=read(),y=read();
G[x].push_back(y);
G[y].push_back(x);
}
for(int i=1;i<=n;++i)W[i]=read();
for(int i=1;i<=m;++i)S[i]=read(),T[i]=read();
dfs1(1,0),dfs2(1,1);
for(int i=1;i<=m;++i)LCA[i]=lca(S[i],T[i]); for(int i=1;i<=m;++i){
if(LCA[i]==T[i]){
tag1[S[i]].push_back((tag){depth[S[i]],1});
tag1[father[T[i]]].push_back((tag){depth[S[i]],-1});
}else if(LCA[i]==S[i]){
tag2[T[i]].push_back((tag){depth[S[i]]-2*depth[LCA[i]],1});
tag2[father[S[i]]].push_back((tag){depth[S[i]]-2*depth[LCA[i]],-1});
}else{
if(W[LCA[i]]+depth[LCA[i]]==depth[S[i]])--cnt[LCA[i]];
tag1[S[i]].push_back((tag){depth[S[i]],1});
tag1[father[LCA[i]]].push_back((tag){depth[S[i]],-1});
tag2[T[i]].push_back((tag){depth[S[i]]-2*depth[LCA[i]],1});
tag2[father[LCA[i]]].push_back((tag){depth[S[i]]-2*depth[LCA[i]],-1});
}
} dfs(1,0,0); for(int i=1;i<=n;++i)printf("%d ",cnt[i]); return 0;
}

  

NOIP2016天天爱跑步解题思路的更多相关文章

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

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

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

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

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

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

  4. BZOJ4719 [Noip2016]天天爱跑步

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000作者博客:http://www.cnblogs.com/ljh2000-jump/转 ...

  5. NOIP2016 天天爱跑步(线段树/桶)

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

  6. ☆ [NOIp2016] 天天爱跑步 「树上差分」

    题目类型:LCA+思维 传送门:>Here< 题意:给出一棵树,有\(M\)个人在这棵树上跑步.每个人都从自己的起点\(s[i]\)跑到终点\(t[i]\),跑过一条边的时间为1秒.现在每 ...

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

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

  8. noip2016天天爱跑步

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

  9. bzoj 4719: [Noip2016]天天爱跑步

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

随机推荐

  1. AutoITx3.DLL所有函数及说明

    AutoItSetOption    调整各种函数/参数的运作方式. BlockInput    屏蔽/启用鼠标与键盘(输入). CDTray    弹出或关闭光驱. ClipGet    获取剪贴板 ...

  2. Python print()函数

    #输出单个数据,会自动输出回车换行 print(1) print(2) #输出 1 2 #输出换行 print('\n') #防止换行 for x in range(0, 5): print(x, e ...

  3. 大部分政府网站U-mail存在直接拿shell漏洞

    大部分网站政府网站U-mail存在直接拿shell漏洞加入webmail/userapply.php?execadd=333&DomainID=111直接爆出物理地址 然后将 aa' unio ...

  4. ch6 列表和导航条

    为列表添加定制的项目符号 可使用list-style-image属性:缺点是对项目符号图像的位置的控制能力不强. 常用的方法:使用list-style-type来关闭项目符号,将定制的项目符号作为背景 ...

  5. PyQt5点击菜单栏弹出新窗口,解决新窗口闪退的实现方法

    实现的功能为:当点击菜单中某个菜单时,会弹出一个新窗口,下面就列出部分代码 def mail_setting(self): log.debug("open mail settings&quo ...

  6. 1-6SpringBoot之事务管理@Transactional

    以前学ssh ssm都有事务管理service层通过applicationContext.xml配置,所有service方法都加上事务操作: 用来保证一致性,即service方法里的多个dao操作,要 ...

  7. C++ Primer Plus 6 笔记(3)

    第5章 1.cout在显示bool值之前将它们转换为int,但cout.setf(ios:: boolalpha)函数调用设置了一个标记,该标记命令cout显示true和false,而不是1和0 2. ...

  8. django+centos+mariadb读写分离完美实现(上)-mysql主从备份实现

    首先画图一张,用来展示今天要做的事情,读写分离,个人理解就是使用mysql主从备份的原理,让两个数据库同时为自己提供服务.其中主库负责数据保存,从库负责数据展示,可以一主一从,也可以一主多从.从而降低 ...

  9. STM32学习笔记:创建工程模板

    STM32作为一类高级单片机,在应用中不容小觑. 所以本人从2018年1月1日开始对于STM32系列单片机的学习. 本人所持的型号为:STM32F429ZI-DISCOVERY,M4内核,封装类型为: ...

  10. ffmpeg 学习:001-搭建开发环境

    介绍 由于命令行的ffmpeg工具无法满足产品的性能要求,需要对视频流进行兼容.所以需要调试有关的参数. FFmpeg全名是Fast Forward MPEG(Moving Picture Exper ...