BZOJ 4719--天天爱跑步(LCA&差分)
Posted Iscream
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 4719--天天爱跑步(LCA&差分)相关的知识,希望对你有一定的参考价值。
4719: [Noip2016]天天爱跑步
Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 1464 Solved: 490
[Submit][Status][Discuss]
Description
Input
Output
输出1行N 个整数,第个整数表示结点的观察员可以观察到多少人。
Sample Input
2 3
1 2
1 4
4 5
4 6
0 2 5 1 2 3
1 5
1 3
2 6
Sample Output
HINT
题目链接:
http://www.lydsy.com/JudgeOnline/problem.php?id=1096
Solution
刚开始学OI的时候就看过这道题。。。当时是贴代码过的。。。。
记得好像是用树链剖分。。不记得了。。反正很长。。。
现在快要noip了。。。把往年的题目拿出来看一下,算是临近noip的梳理吧。。。。。
好多废话
进入正题。。。。。
首先每个人都要走最短路径。。。马上想到LCA。。。不然还能有什么。。。
每个人分别统计显然是不行的,不过似乎可以根据特殊情况水很多分。。
于是想到差分。。
假设第 i 个人的起点为 Si ,终点为 Ti 。。LCA ( Si ,Ti ) = rt
每条线段长度都是 1 ,故路径长度与深度有关。。。
于是路径就可以分成两部分:Si -> rt 和 rt ->Ti
两条路径分开统计。。。
在向上的路径中,比如一个起点 Si ,深度为 dep [ Si ] ,
那么可以在遍历到Si的时候 U [ dep [ Si ] ] ++;
然后在到达 rt 的父亲的时候 U [ dep [ Si ] ] --; (U [ i ]表示某一方向路径的总值)
向下的路径用一样的方法。。。但这样rt 的统计会有重复。。
所以两次统计中要有一次在 rt 的时候就 U [ dep [ Si ] ] - - ;
这样就可以了,虽然分析很长但代码不是很长。。。。
注意在BZOJ上提交不能有末尾空格。。。害我PE了一发。。。
代码
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #define N 400000 using namespace std; inline int Read(){ int x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } int n,m,cnt=0; int fa[N][22]; int hed[N],w[N],dep[N],ans[N],U[N],D[N<<1]; struct edge{ int r,nxt; }e[N<<1]; struct node{ int u,w; }; vectore1[N],e2[N<<1]; void insert(int u,int v){ e[++cnt]=(edge){v,hed[u]};hed[u]=cnt; e[++cnt]=(edge){u,hed[v]};hed[v]=cnt; } void dfs1(int x,int F){ for(int i=1;(1<<i)<=dep[x];i++) fa[x][i]=fa[fa[x][i-1]][i-1]; for(int i=hed[x];i;i=e[i].nxt) if(e[i].r!=F){ dep[e[i].r]=dep[x]+1; fa[e[i].r][0]=x; dfs1(e[i].r,x); } } int lca(int u,int v){ if(dep[u]<dep[v])swap(u,v); int d=dep[u]-dep[v]; for(int i=0;(1<<i)<=d;i++) if((1<<i)&d) u=fa[u][i]; if(u==v)return u; for(int i=20;i>=0;i--){ if((1<<i)>dep[u] || fa[u][i]==fa[v][i])continue; u=fa[u][i];v=fa[v][i]; } return fa[u][0]; } void dfs2(int x,int F){ ans[x]-=U[w[x]+dep[x]]; ans[x]-=D[w[x]-dep[x]+n]; for(int i=0;i<e1[x].size();i++) U[e1[x][i].u]+=e1[x][i].w; for(int i=0;i<e2[x].size();i++) D[e2[x][i].u+n]+=e2[x][i].w; for(int i=hed[x];i;i=e[i].nxt) if(e[i].r!=F) dfs2(e[i].r,x); ans[x]+=U[w[x]+dep[x]]+D[w[x]-dep[x]+n]; } int main(){ int u,v,rt; n=Read();m=Read(); for(int i=1;i<n;i++){ u=Read();v=Read(); insert(u,v); } for(int i=1;i<=n;i++) w[i]=Read(); dfs1(1,0); for(int i=1;i<=m;i++){ u=Read();v=Read(); rt=lca(u,v); e1[u].push_back((node){dep[u],1}); e1[rt].push_back((node){dep[u],-1}); e2[v].push_back((node){dep[u]-(dep[rt]<<1),1}); e2[fa[rt][0]].push_back((node){dep[u]-(dep[rt]<<1),-1}); } dfs2(1,0); for(int i=1;i<n;i++) printf("%d ",ans[i]); printf("%d",ans[n]); return 0; }
This passage is made by Iscream-2001.
以上是关于BZOJ 4719--天天爱跑步(LCA&差分)的主要内容,如果未能解决你的问题,请参考以下文章
bzoj4719: [Noip2016]天天爱跑步 树上差分