【题目描述】

【思路】

这是道好题呀。考虑把一条路径(u,v)拆成两条:从u到lca(u,v),从lca(u,v)到v。下面我们以向上的路径为例讨论做法。对于一条向上的路径,它对一个点x有贡献当且仅当它覆盖了点x且 d e p [ u ] − d e p [ x ] = w [ x ] dep[u]-dep[x]=w[x] dep[u]−dep[x]=w[x]。即对于一个点x,我们需要统计有多少覆盖了它的路径的起点u满足 w [ x ] + d e p [ x ] = d e p [ u ] w[x]+dep[x]=dep[u] w[x]+dep[x]=dep[u]。显然后面这个限制只需要维护一个数据结构就行了。那么问题在于如何使它只统计了覆盖自己的路径。注意到覆盖父亲和儿子的边只有一部分不同,我们可以考虑父亲继承子树信息,然后删除其中没有覆盖自己的路径。对于路径的删除和加入,我们可以用树上差分实现,在u处加入该边,在fa[lca(u,v)]处删除该边。每条路径最多被加入一次,删除一次,所以时间复杂度正确。可以使用线段树合并实现父亲继承子树信息。但是考虑到这是计数问题,并非最优化问题,答案满足可减性。所以我们不需要线段树合并保证只考虑了子树内的路径。我们可以在处理子树之前查询一次答案,在处理子树之后查询一次答案,两次答案的变化量就是子树中的路径对自己的贡献。这样使用一个桶就可以实现上述操作。我们按dfs的顺序依次处理每个点:
1.查询一次答案 a n s 1 ans_1 ans1​。
2.递归处理子树。
3.处理自己。
4.查询一次答案 a n s 2 ans_2 ans2​,这个点的答案即 a n s 2 − a n s 1 ans_2-ans_1 ans2​−ans1​。

对于向下的路径依然可以类似操作,只需要查询满足 d e p [ u ] + d e p [ x ] − 2 ∗ d e p [ l c a ( u , v ) ] = w [ x ] dep[u]+dep[x]-2*dep[lca(u,v)]=w[x] dep[u]+dep[x]−2∗dep[lca(u,v)]=w[x]的路径数量,移项即可维护。同样,我们在v处向桶中加入这条路径,在fa[lca(u,v)]删除这条路径即可。注意,为防止lca处重复统计了两条路径的贡献,我们可以选择一条路径在lca处删除,另一条在fa[lca]处删除。

代码:

#include<bits/stdc++.h>
#define re register
#define F(i,a,b) for(int re i=a;i<=b;++i)
#define D(i,a,b) for(int re i=a;i>=b;--i)
using namespace std;
const int N=6e5+5,M=3e5+5;
inline char nc(){static char buf[100000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int red(){char re ch=nc();int re sum=0;while(!(ch>='0'&&ch<='9'))ch=nc();while(ch>='0'&&ch<='9')sum=((sum<<2)+sum<<1)+(ch^48),ch=nc();return sum;
}
int n,m,a,b;
int to[N],f[M],nxp[N],cnt=0;
inline void add(int u,int v){to[++cnt]=v;nxp[cnt]=f[u];f[u]=cnt;to[++cnt]=u;nxp[cnt]=f[v];f[v]=cnt;
}
struct que{int pos,v;que(int x=0,int y=0){pos=x,v=y;}};
vector<que>g[2][M];
int fa[M],dep[M],top[M],son[M],ans[M],w[M],siz[M],num[2][N];
void dfs1(int u){int v,&x=son[u];siz[u]=1;dep[u]=dep[fa[u]]+1;for(int i=f[u];i;i=nxp[i]){if((v=to[i])==fa[u])continue;fa[v]=u;dfs1(v);siz[u]+=siz[v];if(siz[x]<siz[v])x=v;}
}
void dfs2(int u){if(son[u])top[son[u]]=top[u],dfs2(son[u]);for(int i=f[u];i;i=nxp[i])if(!top[to[i]])dfs2(top[to[i]]=to[i]);
}
inline int lca(int a,int b){while(top[a]^top[b])dep[top[a]]<dep[top[b]]?b=fa[top[b]]:a=fa[top[a]];return dep[a]<dep[b]?a:b;
}
inline int calc(int u){return num[0][w[u]+dep[u]]+num[1][w[u]-dep[u]+M];}
inline void insert(int u,int op){D(i,g[op][u].size()-1,0)num[op][g[op][u][i].pos]+=g[op][u][i].v;}
void dfs3(int u){int ans1=calc(u);for(int re i=f[u];i;i=nxp[i])if(dep[to[i]]>dep[u])dfs3(to[i]);insert(u,0);insert(u,1);ans[u]=calc(u)-ans1;
}
inline void print(int x){if(x>9)print(x/10);putchar(x%10^48);
}
int main()
{n=red();m=red();int rt=1;F(i,2,n)add(red(),red());F(i,1,n)w[i]=red();dfs1(rt),dfs2(top[rt]=rt);F(i,1,m){int u=red(),v=red(),x=lca(u,v),y=fa[x];g[0][u].push_back(que(dep[u],1));g[1][v].push_back(que(dep[u]-2*dep[x]+M,1));g[0][y].push_back(que(dep[u],-1));g[1][x].push_back(que(dep[u]-2*dep[x]+M,-1));}dfs3(rt);F(i,1,n)print(ans[i]),putchar(' ');
}

【NOIP2016】【桶/线段树合并】【树上差分】天天爱跑步相关推荐

  1. 洛谷 - P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并(树上差分+线段树合并)

    题目链接:点击查看 题目大意:给出一棵树,再给出 m 次操作,每次操作会选择两个点 ( x , y ) ,使得这条路径上的所有点的种类 z 加一,最后问每个点的哪个种类出现的频率最高,若多个种类出现频 ...

  2. 雨中的尾巴(线段树合并+树上差分)

    哇这道题 恶心死我 首先要知道,树上差分一般解决的问题是关于树上的覆盖问题 然后遇到覆盖问题尽量不要打树剖(会慢很多) 关于此题 因为这道题覆盖的是 从xxx到yyy的点 所以我们在 x,yx,yx, ...

  3. 线段树分裂与合并 ---- 树上差分 P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并

    题目链接 解题思路: 首先题目是对u,vu,vu,v这两条路径上面添加一个zzz,然后运用树上点的差分思想,对于分发路径u,vu,vu,v,我们在uuu上+1+1+1,在vvv上+1+1+1,在lca ...

  4. 【线段树合并】解题报告:luogu P4556雨天的尾巴 (树上对点差分 + 动态开点 + 线段树合并)线段树合并模板离线/在线详解

    题目链接:雨天的尾巴 本题本身是一个非常简单的一道树上差分的模板题,但是由于变态的数据范围,我们直接用数组是存不下的(本来使用一颗普通的线段树直接维护最大值即可.但是本题的空间只有128MB,直接按照 ...

  5. NOIP2016 天天爱跑步 线段树合并

    题意: 给出一棵n个点的树,以及m次操作,每次操作从起点向终点以每秒一条边的速度移动(初始时刻为0),最后对于每个点询问有多少次操作在经过该点的时刻为某值. (题面太毒瘤建议自己去题库食用) 分析: ...

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

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

  7. 线段树合并与分裂维护树上最长上升子序列 + 点分治删点 ---- 2021 牛客多校第一场 C - Cut the tree(详解)

    题目大意: 给你一个树,树上每个点都有一个权值valnodeval_{node}valnode​,路径(u,v)(u,v)(u,v) 上所有点按顺序有序序列,令f(u,v)f(u,v)f(u,v)是这 ...

  8. 【CF700E】Cool Slogans【后缀自动机】【可持久化线段树合并】【树上倍增】

    传送门 题意:给定字符串SSS,求一堆字符串s1,s2,s3,...,sks_1,s_2,s_3,...,s_ks1​,s2​,s3​,...,sk​,满足s1s_1s1​是SSS的子串,且sis_i ...

  9. BZOJ 4719: [Noip2016]天天爱跑步 线段树合并

    title BZOJ 4719 LUOGU 1600 简化题意: 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每 ...

最新文章

  1. Dora.Interception,为.NET Core度身打造的AOP框架 [1]:更加简练的编程体验
  2. java项目教学_java项目_java项目教程_java项目视频教程 _课课家
  3. 数据结构学习-二维数组与稀疏数组转换
  4. Wi-Fi 协议和数率?
  5. (转)如何在maven的pom.xml中添加本地jar包
  6. python读取指定行到最后一行_python读取文件最后一行两种方法
  7. 最近让我焦灼的四个问题(有解)
  8. IOS开发之----四舍五入问题
  9. 微软api的word在线预览
  10. DCDC与LDO浅析
  11. 【冷冻电镜入门】加州理工公开课课程笔记 Part 3: Image Formation
  12. C语言中char s[] 和 char *s的区别
  13. android代码 发警报音,Android设置多个警报
  14. FX3U和三菱伺服控制的框架标准程序 回原点、JOG手动、绝对定位、相对定位、控制等部分
  15. win10+tensorflow-gpu+1050ti(终于安装成功了T﹏T)
  16. 计算机工资表怎么打,Word怎么制作工资条 Word制作工资条教程-电脑教程
  17. 家庭养花的资料大全-春雷转
  18. 电影《绝望主夫》观后感
  19. 2022-11-16 AndroidS 新建产品lunch
  20. FPGA之BISS接口协议实现

热门文章

  1. Python学习日记(二十九) 网络编程
  2. 微信公众平台回复多条消息php,PHP微信公众平台开发 - 消息回复的封装_PHP教程...
  3. PMP软件开发规模估算——代码行估算、功能点估算、PERT加权估算
  4. 在 M1/M2 Mac 上,让 Windows 11 免费“跑”起来!
  5. [概率期望][树形DP][LCA]JZOJ 5814 树
  6. 数据依赖和控制依赖 Data Dependence and Contol Dependence
  7. 星起航:移动端用户是消费主力,亚马逊卖家应注重移动端页面优化
  8. [AcWing] 1012. 友好城市(C++实现)最长上升子序列模型、较为特殊
  9. C++11实现一个countdown latch
  10. zynq板zedboard+vitis设计(二)AXI DMA