传送门

我们把每一条路径拆成$u->lca$和$lca->v$的路径

先考虑$u->lca$,如果这条路径会对路径上的某一个点产生贡献,那么满足$dep[u]-dep[x]=w[x],dep[u]=dep[x]+w[x]$,注意到$dep[x]+w[x]$是一个定值,所以我们只要去找它的子树里有多少个点的$dep$等于$dep[x]+w[x]$就可以了,这个可以直接开一个桶。然而如果点$x$在$lca$的上面,这一条路径是不会对他产生贡献的,那么我们就得在$lca$处把这一条路径的贡献给减去。具体怎么做呢?我们可以利用树上差分的思想,在点$u$把它的出现次数$+1$,在$lca$处把它的出现次数$-1$,那么我们对于每一个点,只要去查询它的子树里$dep[x]+w[x]$这个值出现了多少次就可以了。

顺便注意一下,因为我们的桶里存的不止是一棵子树的答案,所以查询得到的次数可能是来子其他子树的。那么我们可以dfs进去之前先记录一下,完了之后再记录一下,两次的差就是这个值在其子树里的实际出现次数

然后$lca->v$的路径咋搞嘞?只要把式子改成$dep[v]-dep[x]=len-w[i]$($len$表示路径长度),然后和上面一样的做法。注意这里有可能会出现负数,所以我们要让它加上$3e5$

注意到$lca$会被我们统计两次,那么我们只要最后做完之后,把所有被统计了两次答案的$lca$答案减一就好了

 //minamoto
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[<<],*p1=buf,*p2=buf;
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,:;}
inline int read(){
#define num ch-'0'
char ch;bool flag=;int res;
while(!isdigit(ch=getc()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getc());res=res*+num);
(flag)&&(res=-res);
#undef num
return res;
}
char sr[<<],z[];int C=-,Z;
inline void Ot(){fwrite(sr,,C+,stdout),C=-;}
inline void print(int x){
if(C><<)Ot();if(x<)sr[++C]=,x=-x;
while(z[++Z]=x%+,x/=);
while(sr[++C]=z[Z],--Z);sr[++C]=' ';
}
const int N=,M=;
int head[N],Next[M],ver[M],tot;
int son[N],sz[N],fa[N],dep[N],top[N];
int c[N],w[N],val[N],num[];
int n,m,lim,ans[N];
vector<int> qaq[N],qaq2[N],qaq3[N];
struct node{int s,t,lca,len;}q[N];
inline void add(int u,int v){
ver[++tot]=v,Next[tot]=head[u],head[u]=tot;
ver[++tot]=u,Next[tot]=head[v],head[v]=tot;
}
void dfs1(int u){
sz[u]=,dep[u]=dep[fa[u]]+,cmax(lim,dep[u]);
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(v!=fa[u]){
fa[v]=u,dfs1(v),sz[u]+=sz[v];
if(sz[v]>sz[son[u]]) son[u]=v;
}
}
}
void dfs2(int u,int t){
top[u]=t;
if(son[u]) dfs2(son[u],t);else return;
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
}
inline int LCA(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
void dfs(int u,int fa){
int now=w[u]+dep[u],k;if(now<=lim) k=c[now];
for(int i=head[u];i;i=Next[i]){
int v=ver[i];if(v!=fa) dfs(v,u);
}
c[dep[u]]+=val[u];if(now<=lim) ans[u]=c[now]-k;
for(int i=,s=qaq[u].size();i<s;++i) --c[dep[qaq[u][i]]];
}
void DFS(int u,int fa){
int now=dep[u]-w[u],k;now+=,k=num[now];
for(int i=head[u];i;i=Next[i]){
int v=ver[i];if(v!=fa) DFS(v,u);
}
for(int i=,s=qaq2[u].size();i<s;++i) ++num[+qaq2[u][i]];
ans[u]+=num[now]-k;
for(int i=,s=qaq3[u].size();i<s;++i) --num[+qaq3[u][i]];
}
int main(){
//freopen("testdata.in","r",stdin);
n=read(),m=read();
for(int i=;i<n;++i){
int u=read(),v=read();add(u,v);
}
for(int i=;i<=n;++i) w[i]=read();
dfs1(),dfs2(,);
for(int i=;i<=m;++i){
q[i].s=read(),q[i].t=read(),++val[q[i].s];
q[i].lca=LCA(q[i].s,q[i].t),q[i].len=dep[q[i].s]+dep[q[i].t]-dep[q[i].lca]*;
qaq[q[i].lca].push_back(q[i].s);
}
dfs(,);
for(int i=;i<=m;++i){
qaq2[q[i].t].push_back(dep[q[i].t]-q[i].len);
qaq3[q[i].lca].push_back(dep[q[i].t]-q[i].len);
}
DFS(,);
for(int i=;i<=m;++i) if(dep[q[i].s]-dep[q[i].lca]==w[q[i].lca]) --ans[q[i].lca];
for(int i=;i<=n;++i) print(ans[i]);
Ot();
return ;
}

洛谷 P1600 天天爱跑步(LCA+乱搞)的更多相关文章

  1. 洛谷P1600 天天爱跑步(线段树合并)

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

  2. 洛谷 P1600 天天爱跑步

    https://www.luogu.org/problemnew/show/P1600 (仅做记录) 自己的假方法: 每一次跑从a到b:设l=lca(a,b)对于以下产生贡献: a到l的链上所有的点( ...

  3. 洛谷P1600 天天爱跑步(差分 LCA 桶)

    题意 题目链接 Sol 一步一步的来考虑 \(25 \%\):直接\(O(nm)\)的暴力 链的情况:维护两个差分数组,分别表示从左向右和从右向左的贡献, \(S_i = 1\):统计每个点的子树内有 ...

  4. 洛谷P1600 天天爱跑步——树上差分

    题目:https://www.luogu.org/problemnew/show/P1600 看博客:https://blog.csdn.net/clove_unique/article/detail ...

  5. 洛谷P1600 天天爱跑步

    天天放毒... 首先介绍一个树上差分. 每次进入的时候记录贡献,跟出来的时候的差值就是子树贡献. 然后就可以做了. 发现考虑每个人的贡献有困难. 于是考虑每个观察员的答案. 把路径拆成两条,以lca分 ...

  6. 洛谷P1600 天天爱跑步——题解

    题目传送 首先要考虑入手点.先考虑一个一个玩家处理,显然不加优化的话,时间复杂度是O(n)的.发现对于玩家路径上的点都有一个观察员,一个都不能忽视,看起来是很难优化了.在做题时,发现一个思路很难想,就 ...

  7. 洛谷$P1600$ 天天爱跑步 树上差分

    正解:树上差分 解题报告: 传送门$QwQ$! 这题还挺妙的,,,我想了半天才会$kk$ 首先对一条链$S-T$,考虑先将它拆成$S-LCA$和$LCA-T$,分别做.因为总体上来说差不多接下来我就只 ...

  8. [NOIP 2016D2T2/Luogu P1600] 天天爱跑步 (LCA+差分)

    待填坑 Code //Luogu P1600 天天爱跑步 //Apr,4th,2018 //树上差分+LCA #include<iostream> #include<cstdio&g ...

  9. [luogu]P1600 天天爱跑步[LCA]

    [luogu]P1600 [NOIP 2016]天天爱跑步 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上 ...

随机推荐

  1. (MVC — — Demo)客户管理系统的开发日志

    点击一下 目录 第一步:搭建开发环境 第二步:层次包(按照三层架构思想写) 第四步:开发(utils)工具包 第四步:开发 Dao 层 第五步:开发 services 层 第六步:开发 factory ...

  2. QT QcustomPlot的使用(二)

    在QcustomPlot中,给横纵坐标添加箭头的方法 //在末尾添加箭头 customPlot->xAxis->setUpperEnding(QCPLineEnding::esSpikeA ...

  3. php7和PHP5对比的新特性和性能优化

    1  抽象语法树( AST) 1)在 PHP5中,从 php 脚本到 opcodes 的执行的过程是: Lexing:词法扫描分析,将源文件转换成 token 流:    Parsing:语法分析,在 ...

  4. VS2017的一些调试方法技巧

    一.基本的操作. 1.启动调试. 可以通过VS的调试(Debug)菜单启动调试.点击调试菜单下的“启动调试”或者按F5键启动.如果你已经在代码中加入了断点,那么执行会自动开始. 注:退出调试快捷键sh ...

  5. 【hash】Seek the Name, Seek the Fame

    [哈希和哈希表]Seek the Name, Seek the Fame 题目描述 The little cat is so famous, that many couples tramp over ...

  6. 排序之快排(JS)

    快速排序(Quicksort)是对冒泡排序的一种改进. 它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分 ...

  7. .NET Core 3.0 发布单文件可执行程序

    Windows dotnet publish -r win10-x64 /p:PublishSingleFile=true maxOS dotnet publish -r osx-x64 /p:Pub ...

  8. sql 存储过程笔记3

    16:22 2014/1/26一.定义变量--简单赋值declare @a int set @a = 5 print @a --使用select语句赋值declare @user1 nvarchar( ...

  9. 【Struts2】进阶

    一.Action处理请求参数 1.1 属性驱动 1.2 模型驱动 1.3 扩展 将数据封装到List集合 将数据封装到Map集合 二.类型转换 2.1 自定义类型转换器: 1.创建一个自定义类型转换器 ...

  10. 【转载】python format遇上花括号{}

    在format string中, 大括号已经被format占用,想要使用大括号本身,该怎么办? 以下转载自这里. ============ 分割线 ============ 使用format时,字符串 ...