传送门

我们把每一条路径拆成$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. AndroidStudio下载安装教程(图文教程)

    场景 Android Studio 中文社区: http://www.android-studio.org/ 下载安装包,这里选择64位Windows 等待下载完成. 注: 博客: https://b ...

  2. Java网络编程-你是GG还是MM?

    第六阶段 网络编程 每一台计算机通过网络连接起来,达到了数据互动的效果,而网络编程所解决的问题就是如何让程序与程序之间实现数据的通讯与互动 在吗?你是GG还是MM? (一) 网络模型概述 (1) 两大 ...

  3. notepad++一次多行复制粘贴到对应位置

    NodePad++一次复制多行粘贴到对应位置 有时候要写sql,但是里面有很多字段要对应上,如果要自己一个字段一个字段的去写是在有点麻烦,是不是有更好的方法做到这件事呢? 要做这件事,首先分析下我们已 ...

  4. Kubernetes---Pod phase

    ⒈Pod phase Pod的status字段是一个PodStatus对象,PodStatus中有一个 phase字段. Pod的相位(phase)是Pod 在其生命周期中的简单宏观概述.该阶段并不是 ...

  5. golang之工厂模式

    说明: golang的结构体没有构造函数,通常可以使用工厂模式来解决这个问题 如果包里面的结构体变量首字母小写,引入后,不能直接使用,可以工厂模式解决: ch1.go package ch1 type ...

  6. .Net C# RSA签名和验签

    帮助类 using System; using System.Text; using System.IO; using System.Security.Cryptography; namespace ...

  7. Eclipse中项目本身没有问题,可是工程名却有红色小叉叉解决办法

    右击项目“Properties”,在弹出的“Properties”的左侧边框,单击“Project Facets”,打开“Project Facets”页面, 在页面中“Java”下拉选项中,选择与自 ...

  8. AutoFac实现程序集级别的依赖注入

    1.介绍      所谓程序集级别的依赖注入是指接口和实现的依赖不使用配置文件或硬代码实现(builder.RegisterType<UserInfoService>().As<IU ...

  9. Dubbo相关的基础

    Dubbo是一款高性能轻量级的java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务注册与发现. Dubbo是阿里开源的一个项目,现在已经是Apache的顶级 ...

  10. css 对div用hover设置border,出现抖动和div走位问题,解决方法

    样式设置 : div:hover { border:1px solid red;} 当鼠标移动到div时,产生抖动和偏移. 产生的原因: 是因为设置border时设置了1px边框,多出的这1px,与其 ...