洛谷 P1600 天天爱跑步(LCA+乱搞)
我们把每一条路径拆成$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+乱搞)的更多相关文章
- 洛谷P1600 天天爱跑步(线段树合并)
小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 nn ...
- 洛谷 P1600 天天爱跑步
https://www.luogu.org/problemnew/show/P1600 (仅做记录) 自己的假方法: 每一次跑从a到b:设l=lca(a,b)对于以下产生贡献: a到l的链上所有的点( ...
- 洛谷P1600 天天爱跑步(差分 LCA 桶)
题意 题目链接 Sol 一步一步的来考虑 \(25 \%\):直接\(O(nm)\)的暴力 链的情况:维护两个差分数组,分别表示从左向右和从右向左的贡献, \(S_i = 1\):统计每个点的子树内有 ...
- 洛谷P1600 天天爱跑步——树上差分
题目:https://www.luogu.org/problemnew/show/P1600 看博客:https://blog.csdn.net/clove_unique/article/detail ...
- 洛谷P1600 天天爱跑步
天天放毒... 首先介绍一个树上差分. 每次进入的时候记录贡献,跟出来的时候的差值就是子树贡献. 然后就可以做了. 发现考虑每个人的贡献有困难. 于是考虑每个观察员的答案. 把路径拆成两条,以lca分 ...
- 洛谷P1600 天天爱跑步——题解
题目传送 首先要考虑入手点.先考虑一个一个玩家处理,显然不加优化的话,时间复杂度是O(n)的.发现对于玩家路径上的点都有一个观察员,一个都不能忽视,看起来是很难优化了.在做题时,发现一个思路很难想,就 ...
- 洛谷$P1600$ 天天爱跑步 树上差分
正解:树上差分 解题报告: 传送门$QwQ$! 这题还挺妙的,,,我想了半天才会$kk$ 首先对一条链$S-T$,考虑先将它拆成$S-LCA$和$LCA-T$,分别做.因为总体上来说差不多接下来我就只 ...
- [NOIP 2016D2T2/Luogu P1600] 天天爱跑步 (LCA+差分)
待填坑 Code //Luogu P1600 天天爱跑步 //Apr,4th,2018 //树上差分+LCA #include<iostream> #include<cstdio&g ...
- [luogu]P1600 天天爱跑步[LCA]
[luogu]P1600 [NOIP 2016]天天爱跑步 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上 ...
随机推荐
- C#中异步编程异常的处理方式
异步编程异常处理 在同步编程中,一旦出现错误就会抛出异常,我们可以使用try-catch来捕捉异常,未被捕获的异常则会不断向上传递,形成一个简单而统一的错误处理机制.但是对于异步编程来说,异常处理一直 ...
- 搭建 python 3.5+pycharm 2017.1.3+django 1.12.0 首次 将sqlite3 迁移到mysql
- 【Trie】Immediate Decodability
[题目链接]: https://loj.ac/problem/10052 [题意]: 就是给一些串,是否存在两个串是相同前缀的. [题解] 模板题,不想解释了. [代码]: #include<c ...
- 怎样获取从服务器返回的xml或html文档对象
使用 xhr.responseXML; 通过这个属性正常获取XML或HTML文档对象有两个前置条件: 1. Content-Type头信息的值等于: text/xml 或 application/x ...
- django管理系统代码优化-分组(二)
django管理系统代码优化-分组(二) 后续进行代码更新,优化 一优化的内容 优化前代码:https://www.cnblogs.com/pythonywy/p/11345626.html 路由进行 ...
- 关于windows下编写的shell脚本在linux下无法运行报错问题
首先,你写的shell脚本必须是正确的, 其次,无法运行的原因:因为windows下的换行是两个字节,而你上传到linux,linux下换行是两个字节,所以编译的酒不正确的,导致无法 运行脚本, 这种 ...
- 安装笔记, caffe 、 opencv等
1. 1.1 opencv static linux mkdir build & cd build cmake .. -LH 这句话用来查看编译选项 如果不知道编译啥 可以用这个查看一下 ...
- R 读取xls/xlsx文件
包readxl install.packages('readxl',repois='https://mirrors.utsc.edu.cn/CRAN/) library(readxl) # read_ ...
- 如何在调用Marketing Cloud contact创建API时增加对扩展字段的支持
需求:扩展字段"微信ID"是我创建出来的extension field,我想用Marketing Cloud提供的contact creation API,在创建contact时也 ...
- SpringCloud之RabbitMQ安装
本文介绍Linux以及MAC OS下的RabbitMQ安装及配置: 一.Linux环境下的RabbitMQ安装(CentOS) 1.安装ErLang Erlang(['ə:læŋ])是一种通用的面向并 ...