洛谷 1600 (NOIp2016) 天天爱跑步——树上差分
题目:https://www.luogu.org/problemnew/show/P1600
看TJ:https://blog.csdn.net/clove_unique/article/details/53427248
树上差分真好。
首先要发现向上和向下的……是定值。然后想到可以差分。
本题的差分略特殊之处在于它的对象是一条连到根的链。把链上的差分值记到非根的端点上。
总之看明白TJ之后感觉真精妙。
而且 从各种中选自己需要的 只需把所有都加进桶中!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=3e5+,M=9e5+;
int n,m,head[N],xnt,w[N],d[N],hd[N],xt,fa[N],tot0,tot1,dfn[N],tim;
int cnt[M],fx=N,ans[N],nw,fatr[N];
struct Edge{
int next,to;Edge(int n=,int t=):next(n),to(t) {}
}edge[N<<];
struct Ed{
int next,to;bool fx;Ed(int n=,int t=,bool f=):next(n),to(t),fx(f) {}
}ed[N<<];
struct Node{
int t,val,s;Node(int t=,int v=,int s=):t(t),val(v),s(s) {}
}a0[N<<],a1[N<<];
void add(int x,int y)
{
edge[++xnt]=Edge(head[x],y);head[x]=xnt;
edge[++xnt]=Edge(head[y],x);head[y]=xnt;
}
void ad(int x,int y)
{
ed[++xt]=Ed(hd[x],y,);hd[x]=xt;
if(x!=y)ed[++xt]=Ed(hd[y],x,);hd[y]=xt;//x!=y!!
}
int find(int a){return fa[a]==a?a:fa[a]=find(fa[a]);}
bool cmp(Node x,Node y){return dfn[x.s]<dfn[y.s];}
void build(int s,int t,int f)
{
a0[++tot0]=Node(,,s);
if(f!=)a0[++tot0]=Node(d[s]-d[f]+,-,fatr[f]);//f!=1 //用fatr
a1[++tot1]=Node(d[s]-d[f]-d[f],-,f);
a1[++tot1]=Node(d[s]-d[f]-d[f],,t);//不要+d[t]-d[f]
}
void dfs(int cr,int f)
{
d[cr]=d[f]+;dfn[cr]=++tim;fatr[cr]=f;
for(int i=hd[cr],v;i;i=ed[i].next)
{
if(dfn[v=ed[i].to])
{
if(ed[i].fx)build(v,cr,find(v));
else build(cr,v,find(v));
}
}
for(int i=head[cr],v;i;i=edge[i].next)
if((v=edge[i].to)!=f)dfs(v,cr),fa[v]=cr;
}
void dfs0(int cr,int f)
{
int pd=d[cr]+w[cr],cpy=cnt[pd];
while(nw<=tot0&&a0[nw].s==cr)cnt[a0[nw].t+d[cr]]+=a0[nw].val,nw++;//+=val
for(int i=head[cr],v;i;i=edge[i].next)
if((v=edge[i].to)!=f)dfs0(v,cr);
ans[cr]+=cnt[pd]-cpy;
}
void dfs1(int cr,int f)
{
int pd=w[cr]-d[cr],cpy=cnt[pd+fx];
while(nw<=tot1&&a1[nw].s==cr)cnt[a1[nw].t+fx]+=a1[nw].val,nw++;
for(int i=head[cr],v;i;i=edge[i].next)
if((v=edge[i].to)!=f)dfs1(v,cr);
ans[cr]+=cnt[pd+fx]-cpy;
}
int main()
{
scanf("%d%d",&n,&m);int x,y;
for(int i=;i<n;i++)
{
scanf("%d%d",&x,&y);add(x,y);fa[i]=i;
}
fa[n]=n;
for(int i=;i<=n;i++)scanf("%d",&w[i]);
for(int i=;i<=m;i++)
{
scanf("%d%d",&x,&y);ad(x,y);
}
d[]=-;dfs(,);sort(a0+,a0+tot0+,cmp);
sort(a1+,a1+tot1+,cmp);
nw=;dfs0(,);memset(cnt,,sizeof cnt);
nw=;dfs1(,);
for(int i=;i<=n;i++)printf("%d ",ans[i]);
return ;
}
然后在洛谷上看到了“文文殿下”的代码。跑得好快!学了一下。
注意到更新cnt值的时候只用和自己节点有关的东西更新。
所以与其按dfs序排序然后在a[ ]上移动,不如给每个点记一个邻接表,指向a[ ]上的位置。
感觉人家对邻接表理解深刻。那个nxt是记在 t [ ] 的角标上的,hdhd提供一个指向 t [ ] 某个角标的入口一样的东西。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=3e5+,M=9e5+,Mm=6e5+;
int n,m,head[N],xnt,w[N],d[N],hd[N],xt,fa[N];
int cnt0[Mm],cnt1[M],fx=N,ans[N],fatr[N];
int hdhd0[N][],hdhd1[N][],t[N<<],nxt[N<<],tot;
bool vis[N];
struct Edge{
int next,to;Edge(int n=,int t=):next(n),to(t) {}
}edge[N<<];
struct Ed{
int next,to;bool fx;Ed(int n=,int t=,bool f=):next(n),to(t),fx(f) {}
}ed[N<<];
void add(int x,int y)
{
edge[++xnt]=Edge(head[x],y);head[x]=xnt;
edge[++xnt]=Edge(head[y],x);head[y]=xnt;
}
void ad(int x,int y)
{
ed[++xt]=Ed(hd[x],y,);hd[x]=xt;
if(x!=y)ed[++xt]=Ed(hd[y],x,),hd[y]=xt;//x!=y!!
}
int find(int a){return fa[a]==a?a:fa[a]=find(fa[a]);}
void adad(int &x,int z){t[++tot]=z;nxt[tot]=x;x=tot;}
void build(int s,int t,int f)
{
adad(hdhd0[s][],);if(f!=)adad(hdhd0[fatr[f]][],d[s]-d[f]+);
adad(hdhd1[f][],d[s]-*d[f]);adad(hdhd1[t][],d[s]-*d[f]);
}
void dfs(int cr,int f)
{
d[cr]=d[f]+;vis[cr]=;fatr[cr]=f;
for(int i=hd[cr],v;i;i=ed[i].next)
if(vis[v=ed[i].to])
if(ed[i].fx)build(v,cr,find(v));
else build(cr,v,find(v));
for(int i=head[cr],v;i;i=edge[i].next)
if((v=edge[i].to)!=f)dfs(v,cr),fa[v]=cr;
}
void dfsx(int cr,int f)
{
int pd0=d[cr]+w[cr],pd1=w[cr]-d[cr];
ans[cr]-=cnt0[pd0]+cnt1[pd1+fx];
for(int i=hdhd0[cr][];i;i=nxt[i])cnt0[t[i]+d[cr]]++;
for(int i=hdhd0[cr][];i;i=nxt[i])cnt0[t[i]+d[cr]]--;
for(int i=hdhd1[cr][];i;i=nxt[i])cnt1[t[i]+fx]++;
for(int i=hdhd1[cr][];i;i=nxt[i])cnt1[t[i]+fx]--;
for(int i=head[cr];i;i=edge[i].next)
if(edge[i].to!=f)dfsx(edge[i].to,cr);
ans[cr]+=cnt0[pd0]+cnt1[pd1+fx];
}
int main()
{
scanf("%d%d",&n,&m);int x,y;
for(int i=;i<n;i++)
{
scanf("%d%d",&x,&y);add(x,y);fa[i]=i;
}
fa[n]=n;
for(int i=;i<=n;i++)scanf("%d",&w[i]);
for(int i=;i<=m;i++)
{
scanf("%d%d",&x,&y);ad(x,y);
}
d[]=-;dfs(,);dfsx(,);
for(int i=;i<=n;i++)printf("%d ",ans[i]);
return ;
}
洛谷 1600 (NOIp2016) 天天爱跑步——树上差分的更多相关文章
- NOIP2016 天天爱跑步 (树上差分+dfs)
题目大意:给你一颗树,树上每个点都有一个观察员,他们仅会在 w[i] 时刻出现,观察正在跑步的玩家 一共有m个玩家,他们分别从节点 s[i] 同时出发,以每秒跑一条边的速度,沿着到 t[i] 的唯一路 ...
- [NOIP2016]天天爱跑步(树上差分+线段树合并)
将每个人跑步的路径拆分成x->lca,lca->y两条路径分别考虑: 对于在点i的观察点,这个人(s->t)能被观察到的充要条件为: 1.直向上的路径:w[i]=dep[s]-dep ...
- NOIP2016 天天爱跑步 - 树上差分
传送门 题目分析: 一年前还是个傻子的时候居然直接放弃了这题. 首先列出两个方程:如果i节点的观察员能够观察到由s->t的那个人,那么: \[dep[s] - dep[i] = w[i], de ...
- 洛谷$P1600$ 天天爱跑步 树上差分
正解:树上差分 解题报告: 传送门$QwQ$! 这题还挺妙的,,,我想了半天才会$kk$ 首先对一条链$S-T$,考虑先将它拆成$S-LCA$和$LCA-T$,分别做.因为总体上来说差不多接下来我就只 ...
- 洛谷P1600 天天爱跑步——树上差分
题目:https://www.luogu.org/problemnew/show/P1600 看博客:https://blog.csdn.net/clove_unique/article/detail ...
- NOIP2016 Day1 T2 天天爱跑步(树上差分,LCA)
原文链接 原题链接 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏 ...
- 洛谷P2680 运输计划(倍增LCA + 树上差分 + 二分答案)
[题目链接] [思路]: 根据题意可以明显看出,当所有任务都完成时的时间是最终的结果,也就是说本题要求,求出最小的最大值. 那这样的话就暗示了将答案二分,进行check. [check方法]: 如果说 ...
- 洛谷P2680 运输计划 [LCA,树上差分,二分答案]
题目传送门 运输计划 Description 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n?1 条双向航道,每条航道建立在两个星球之间, 这 n?1 条航道连通了 L 国的所 ...
- 【LG1600】[NOIP2016]天天爱跑步
[LG1600][NOIP2016]天天爱跑步 题面 洛谷 题解 考虑一条路径\(S\rightarrow T\)是如何给一个观测点\(x\)造成贡献的, 一种是从\(x\)的子树内出来,另外一种是从 ...
随机推荐
- idea软件编码已经设置好了为utf-8,但是svn中down下来的文件格式本身不是utf-8的,此时打开后会出现中文乱码解决方法
我是个idea的忠实用户,新公司的项目都是用eclipse做的,通过svn拉下代码后发现,注释的内容里,中文内容都是乱码.问过项目负责人,说可能是GBK编码. 但是,我通过idea的setting设置 ...
- try中的return语句,在finally前执行还是在finally后执行?
try中有的return语句,也有finally语句,请问finally是否执行,如果执行的话finally在return前执行还是在return后执行? 答案:finally的内容会执行,并且在re ...
- 编辑器上的vim插件,常用到的快捷件
[vim]常用到的插件命令 [Esc] 退出编辑(输入,插入)模式,回到命令模式中(常用)i, I进入插入模式(Insert mode):i 为『从目前光标所在处插入』, I 为『在目前所在行的第一个 ...
- idea maven打包 install 报错The packaging for this project did not assign a file to the build artifact
如题,这其实是个低级错误,这个错的意思是,找不到这个插件的包. 原因很简单,不是找不到这个打包插件,而是自己的项目没有从maven仓库里加载这个包到项目里,因此会找不到. 看一下问什么会报这个错: 大 ...
- 【Hive】任务空间不足错误
hive中跑任务的时候报错 2014-10-09 10:40:27,368 Stage-1 map = 100%, reduce = 32%, Cumulative CPU 2772.48 sec 2 ...
- Cannot run program "svn" (in directory "G:\IntelliJ IDEA 2017.2.5"): CreateProcess error=2, 系统找不到指定的文件。
- c#中绝对路径和相对路径
文件操作涉及一个非常重要的概念——文件路径.文件路径是指用来标识系统中文件存放位置的字符串.如:D:\\test.txt,表示在D盘根目录下存入test.txt文件. 文件路径分为绝对路径和相对路径. ...
- .net学习路线(转)
入门篇1. 学习面向对象(OOP)的编程思想 许多高级语言都是面向对象的编程,.NET也不例外.如果您第一次接触面向对象的编程,就必须理解类.对象.字段.属性.方法和事件.封装.继承和 ...
- Http权威指南(报文)
1.报文的组成 报文由 起始行.首部.主体(可选)组成 请求报文: <method> <request-URL> <version> <headers> ...
- Tomcat应用服务器被黑客 肉鸡攻击 记录
线上一台应用服务器报警,负载过高,这个就诡异了,因为只是一个普通的服务器,应用使用人员不到10个人,咋会负载高,肯定有问题哪,登陆上去查看, top查看哪个占据的cpu资源比较多 [root@aew0 ...