\(NOIP2016\)天天爱跑步

真\(™\)是\(NOIP\)近几年中最难的一道……

先看一下部分分,有起点终点为根节点的分值,想到拆路径,对于路径\((S,T)\),拆为\((S,LCA)\)和\((T,LCA)\)。

首先明确一点,可能有多条路径有相同的起点,\(LCA\)或终点。

先考虑\((S,LCA)\)这一半,一条路径只会对路径上的点产生贡献,不妨设有一个点\(x\)在这一半上。

则这条路径对\(x\)有贡献,当且仅当满足\(d[u]=w[x]+d[x]\)时,发现对于一个点(观察员),\(w[x]+d[x]\)为定值。

对于一个点\(x\),一条路径对其有贡献当且仅当这条路径的\(LCA\)为\(x\)或这条路径仅有一个端点在\(x\)的子树中(这样的路径\(LCA\)在\(x\)之上)

如图,蓝色和绿色的路径会产生贡献,而红色则不会。所以我们可以\(Dfs\)统计答案,即查看有多少个在\(x\)的子树中的路径起点的\(d[u]\)等于\(w[x]+d[x]\),这样统计出的答案只会多不会少,我们考虑如何去掉多余的答案。

先看一下实现过程,先开一个\(Bag[]\)记录像\(d[u]\)这样的值,在\(x\)的子树中处理完之后只要提出\(Bag[w[x]+d[x]]\)的答案即可,但这样我们会发现会统计进起点根本不是\(x\)子树中的路径,因此我们先记录下到\(x\)时\(Bag[w[x]+d[x]]\)的值,在处理完子树之后再将\(Bag[w[x]+d[x]]\)的值和原值相减即可。

但是还有像红色这样的路径会被考虑,所以在跑\(x\)的子树中的节点(设为\(y\))时,我们将以\(y\)为\(LCA\)的路径从桶中减去即可,这样我们统计到的答案就没有多余的了。

同样,考虑\((T,LCA)\)时,我们要满足的式子为:\(w[x]-d[x]=d[u]-2*d[LCA]\)即可。(注意这里可能出现负数,我们加上\(N_{max}\))即可。

还有一点,在计算\(x\)的答案时,不要忘了以\(x\)为路径起点或终点时可以对上面产生贡献,要同时更新\(Bac\)中的值。

做完之后,所有路径\(LCA\)的点答案会被算两次,要减掉

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{int f=1,w=0;char x=0;while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}return w*f;
}
const int N=300010,M=1000010;
int n,m,num_edge,MD;
int las[N][21],ans[N],boki[N];
vector<int> Lex[N],End[N],LCA[N];
int head[N],Dep[N],W[N],Bag[M];
struct Edge{int next,to;} edge[N<<1];
struct Group{int from,to,lca;} Ply[N];
inline void Add(int from,int to)
{edge[++num_edge].next=head[from];edge[num_edge].to=to;head[from]=num_edge;
}
inline void Dfs(int pos,int fa)
{las[pos][0]=fa;Dep[pos]=Dep[fa]+1;for(int i=0;i<20;i++) las[pos][i+1]=las[las[pos][i]][i];for(int i=head[pos];i;i=edge[i].next)if(edge[i].to!=fa) Dfs(edge[i].to,pos);
}
inline int LCA_Ask(int u,int v)
{if(Dep[u]<Dep[v]) swap(u,v);for(int i=20;i>=0;i--) if(Dep[v]<=Dep[u]-(1<<i)) u=las[u][i];if(u==v) return u;for(int i=20;i>=0;i--) if(las[u][i]!=las[v][i]) u=las[u][i],v=las[v][i];return las[u][0];
}
inline void Dfs_For_From(int pos,int fa)//统计路径(S,LCA)的答案
{int Num=Dep[pos]+W[pos],Now;if(Num<=MD) Now=Bag[Num];for(int i=head[pos];i;i=edge[i].next)if(edge[i].to!=fa) Dfs_For_From(edge[i].to,pos);Bag[Dep[pos]]+=boki[pos];//以pos为起点的路径对上面的点产生的贡献if(Num<=MD) ans[pos]=Bag[Num]-Now;for(int i=0;i<(int)Lex[pos].size();i++) Bag[Dep[Lex[pos][i]]]--;//以pos为LCA的路径对上面的点不会在产生贡献了
}
inline void Dfs_For_To(int pos,int fa)//统计路径(LCA,T)的答案
{ int Num=Dep[pos]-W[pos]+N,Now;Now=Bag[Num];for(int i=head[pos];i;i=edge[i].next)if(edge[i].to!=fa) Dfs_For_To(edge[i].to,pos);for(int i=0;i<(int)End[pos].size();i++) Bag[N+End[pos][i]]++;//以pos为终点的路径对上面的点产生的贡献ans[pos]+=Bag[Num]-Now;for(int i=0;i<(int)LCA[pos].size();i++) Bag[N+LCA[pos][i]]--;//以pos为LCA的路径对上面的点不会在产生贡献了
}
main(){
#ifndef ONLINE_JUDGEfreopen("A.in","r",stdin);//Ans=2 0 0 1 1//freopen("B.in","r",stdin);//Ans=1 2 1 0 1freopen("A.out","w",stdout);
#endifn=read(),m=read();MD=-1;for(int i=1,u,v;i<n;i++)u=read(),v=read(),Add(u,v),Add(v,u);for(int i=1;i<=n;i++) W[i]=read();Dfs(1,0);for(int i=1;i<=n;i++) MD=max(MD,Dep[i]);for(int i=1;i<=m;i++){Ply[i].from=read(),Ply[i].to=read();boki[Ply[i].from]++;Ply[i].lca=LCA_Ask(Ply[i].from,Ply[i].to);Lex[Ply[i].lca].push_back(Ply[i].from);End[Ply[i].to].push_back(2*Dep[Ply[i].lca]-Dep[Ply[i].from]);LCA[Ply[i].lca].push_back(2*Dep[Ply[i].lca]-Dep[Ply[i].from]);}Dfs_For_From(1,0);memset(Bag,0,sizeof(Bag));Dfs_For_To(1,0);for(int i=1;i<=m;i++)if(Dep[Ply[i].from]-Dep[Ply[i].lca]==W[Ply[i].lca])ans[Ply[i].lca]--;//减去路径LCA答案重复计算的部分for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
}

转载于:https://www.cnblogs.com/wo-shi-zhen-de-cai/p/11319754.html

$NOIP2016$天天爱跑步相关推荐

  1. NOIP2016天天爱跑步

    NOIP2016天天爱跑步 这题一看显然lca+树上差分,但是因为有w的限制不能直接加,所以考虑权值线段树合并, 每个选手的起点终点对于不同的节点的影响是不同的,这就非常麻烦了,但是可以发现无论如何他 ...

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

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

  3. [NOIP2016]天天爱跑步(lca+乱搞)

    2557. [NOIP2016]天天爱跑步 时间限制:2 s   内存限制:512 MB [题目描述] 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑 ...

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

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

  5. [NOIP2016]天天爱跑步 题解(树上差分) (码长短跑的快)

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

  6. [noip2016]天天爱跑步

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

  7. 【题解】NOIP-2016 天天爱跑步

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

  8. [noip2016]天天爱跑步(主席树+lca)

    恩..在百度的第一页翻了翻,没有用主席树做的,于是打算水一篇blog: 天天爱跑步 首先有一个解题的关键: 将玩家的向上和向下分成两部分: 先定义几个变量,s是起点,t是终点,wi是每个观察员出现的时 ...

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

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

最新文章

  1. IDEA中将代码块封装为方法,IDEA代码重构快捷键
  2. 社工库网址与制作方法
  3. 北美暴风雨,Linux5.12被延迟6天发布
  4. cgo 解决 error while loading shared libraries: xxx.so.x
  5. Sql Create Function简单例子
  6. mysql怎么求平均体重_怎样计算平均身高和平均体重
  7. vmware挂载共享目录
  8. 五款app原型设计工具对比
  9. Java从入门到精通 第23章 文件IO操作
  10. java计算机毕业设计高考填报信息系统源码+数据库+系统+lw文档+部署
  11. 【STC15】串行口1的相关寄存器解读
  12. latex引用文献,带DOI
  13. 欧创芯原装OC 6700B升压型恒流驱动芯片,智能过温保护,内置 VDD 稳压管
  14. 国产操作系统Deepin的安装
  15. This'is wath!
  16. # Alpha冲刺之事后诸葛亮
  17. C#查找Excel重复值(多列)
  18. Unity隐藏目录和隐藏文件
  19. echarts x 起始_echarts中如何在dataZoom 最左侧和最右侧始终显示起始和结束的值
  20. C语言 用switch语句算工资

热门文章

  1. Antd RangePicker 时间日期选择器 禁用当前时间往后的日期及现在时间往后的时分秒禁选
  2. Ngrok服务器+客户端搭建教程
  3. postgre创建存储过程_postgre 存储过程
  4. EDIUS中的GPU转场该怎么设置
  5. 阿里巴巴王坚:不理解在线,就没有真懂互联网
  6. Linux QT 4G发送HTTP POST请求发送JSON格式的数据
  7. 腾讯王巨宏:开源是一项长跑,与开发者共赢开源未来
  8. 电源系统分析之电源电路原理
  9. stm32流水灯程序设计实现
  10. 【spss统计分析】#1数据的录入