本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

Description

小c同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。天天爱跑步是一个养成类游戏,需要
玩家每天按时上线,完成打卡任务。这个游戏的地图可以看作一一棵包含 N个结点和N-1 条边的树, 每条边连接两
个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从1到N的连续正整数。现在有个玩家,第个玩家的
起点为Si ,终点为Ti  。每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发, 以每秒跑一条边的速度,
不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务。 (由于地图是一棵树, 所以
每个人的路径是唯一的)小C想知道游戏的活跃度, 所以在每个结点上都放置了一个观察员。 在结点的观察员会选
择在第Wj秒观察玩家, 一个玩家能被这个观察员观察到当且仅当该玩家在第Wj秒也理到达了结点J  。 小C想知道
每个观察员会观察到多少人?注意: 我们认为一个玩家到达自己的终点后该玩家就会结束游戏, 他不能等待一 段时
间后再被观察员观察到。 即对于把结点J作为终点的玩家: 若他在第Wj秒重到达终点,则在结点J的观察员不能观察
到该玩家;若他正好在第Wj秒到达终点,则在结点的观察员可以观察到这个玩家。

Input

第一行有两个整数N和M 。其中N代表树的结点数量, 同时也是观察员的数量, M代表玩家的数量。
接下来n-1 行每行两个整数U和V ,表示结点U 到结点V 有一条边。
接下来一行N 个整数,其中第个整数为Wj , 表示结点出现观察员的时间。
接下来 M行,每行两个整数Si和Ti,表示一个玩家的起点和终点。
对于所有的数据,保证 。
1<=Si,Ti<=N,0<=Wj<=N
 

Output

输出1行N 个整数,第个整数表示结点的观察员可以观察到多少人。

 

Sample Input

6 3
2 3
1 2 
1 4 
4 5 
4 6 
0 2 5 1 2 3 
1 5 
1 3 
2 6

Sample Output

1 2 1 0 1

HINT

对于1号点,Wi=0,故只有起点为1号点的玩家才会被观察到,所以玩家1和玩家2被观察到,共有2人被观察到。
对于2号点,没有玩家在第2秒时在此结点,共0人被观察到。
对于3号点,没有玩家在第5秒时在此结点,共0人被观察到。
对于4号点,玩家1被观察到,共1人被观察到。
对于5号点,玩家1被观察到,共1人被观察到。
对于6号点,玩家3被观察到,共1人被观察到。
 
 
正解:$lca$+统计
解题报告:

25分

  考虑此时$n$很小,可以对于每条路径上暴力模拟,经过某个点时可以看一下当前时刻,是否跟经过的点的$w$相等,如果相等,则贡献加一。

45分

  注意到测试点$9-12$时,保证$m$条路径的出发点都是$1$,那么我们可以考虑如果将$1$作为树根,那么一条路径怎样才能对于它经过的点产生贡献。

  不难看出对于一个点$i$,只有在$deep[i]=w[i]$,才有可能有贡献。

  我在考场上是直接用的链剖$+$线段树,因为这就变成模板题了,而且$n$不到$10w$,尽管复杂度偏高,但是不易错。

  直接对于每条路径经过的点在线段树上增加$1$次经过次数,显然只有$deep$与$w$相等的点才会产生贡献。

  事实上对于$S=1$的情况有线性的算法,正解会详细介绍,不再赘述。

60分

  注意到测试点$6-8$时,题目保证树退化成链。我们观察一下对于链而言,有什么特别的地方。首先要明确,此时m条路径在链上肯定是要么往左要么往右,即$S<=T$或者$S>T$。

  先只考虑$S<=T$的情况,如果对于$S$到$T$之间的点i,要产生贡献的话,肯定满足$i-S=w[i]$,移项可得$S=i-w[i]$时才可以满足要求。

  注意到等式右边只与$i$本身有关,不妨设为$K[i]$,所以题目变成了查询$S$到$T$之间$K[i]$等于$S$的$i$的数量。

  因为题目只涉及到首和尾,我们可以很容易联想到差分,即对于$S$打上$+1$标记,$T$打上$-1$标记。

  根据上述思路,我们考虑具体做法:对于每个点$i$,我们很容易发现只有从一个特定的点出发才有可能对$i$产生贡献。

  我们考虑维护一个统计数组$A$,$A[k]$表示的是处理到当前的结点时,从$k$出发的路径(而且还没有走到终点)有多少条。

  这样对于每个点$i$,我们只要查询一下所对应的$A[K[i]]$就可以了,根据上面的分析,这就是我们的答案了。

  有一点注意处理:处理一个点$i$时,我们需要把以$i$为起点的路径加入统计数组$A$,再计算这个结点的贡献,最后再把以这个结点为终点的路径从$A$中消除,具体可以用$vector$实现(上述处理顺序的必要性仔细想想就很容易想通了)。

  而对于$S>T$的情况完全类似,只是需要把$K[i]$定义为$i+w[i]$,其余做法完全类似。

100分

  题目中设计的几个档次的部分分其实暗示已经很明显了。

  链的做法离正解就不远了。

  而$S=1$和$T=1$是在告诉我们什么呢?

  拆路径!

  很容易发现,一条$S$到$T$的路径可以拆成一条$S$到$LCA$的路径和$LCA$到$T$的路径,然后对于这两条路径,一条往上,一条往下,都可以对应成链的处理方式了!

  考虑对于每条路径,先将其拆分成两条路径(为了简化对$LCA$在两条路径中都出现的各种情况,我们可以先就让$LCA$出现两次,如果最后发现$LCA$是有贡献的,只需$-1$即可),同样,我们先只考虑向上的路径。

  如果我们对于$S$在统计数组$A$上打上$1$的标记,$LCA$在统计数组$A$上打上$-1$的标记,那么题目转化为求一个点的子树和。

  考虑上述做法正确性:因为只有$S$到$LCA$路径之间的点会产生贡献,而当这个点位于路径之间时,子树和会产生$1$的贡献,而在$S$的子树中或者$LCA$的上方都不会产生贡献。

  具体实现呢?

  对于一个点$i$,产生贡献的条件是$deep[S]-deep[i]=w[i]$,同样令$K[i]=deep[i]+w[i]$,当我们$dfs$到$i$时查询$A[k[i]]$的值即为贡献。

  为了保证正确性,我们思考统计答案的方式和顺序。

  首先我们肯定是在处理完$i$的子树之后再来处理$i$(想想就知道了),然后我们需要再把以$i$出发的向上的路径加入统计数组,再进行查询,最后把以$i$为终点的路径所产生的贡献在统计数组$A$中消除即可。

  注意到我们上面维护的仅仅是一个点的深度,由于同一深度的点很多,所以我们查询的时候会发现会把不在同一子树的点统计入答案,那怎么办呢?我们考虑对于一个点要查询子树和,肯定是只要单独地考虑这一个子树的贡献,所以我们可以记录进入$i$时$A[k[i]]$的值,再在访问完$i$的子树之后统计答案时,看一下此时新的$A[k[i]]$的值。

  容易发现新的值减掉进入时的,才是真正的$i$的子树中的$A[k[i]]$的值。

  这样我们就可以避免把别的子树的答案统计进来了。

  对于向下的点做法类似,有一点复杂的地方就是等式变成了$deep[T]-deep[i]=len-w[i]$($len$为路径长度),发现如果这样做的话会出现负数,那么我们就把统计数组向右平移$3*10^5$位就可以了。

  上述做法如果采用的是倍增求$LCA$的话,复杂度就是$O(nlogn)$;

  如果用$tarjan$离线求$LCA$的话,可以做到$O(n+m)$。

注意事项

  对于树上每个结点,统计答案时不能直接查询在统计数组中的对应的路径条数,

  而应该统计$dfs$进入$i$时,和访问完$i$的子树时的变化量。

 
 
 
  $O(nlogn):$
 //It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
using namespace std;
typedef long long LL;
const int MAXN = ;
const int MAXM = ;
int n,m,ecnt,first[MAXN],next[MAXM],to[MAXM],f[MAXN][],deep[MAXN],ans[MAXN],val[MAXN],tong[MAXN],MAXD,w[MAXN],num[];
vector<int>ljh[MAXN],ljh2[MAXN],ljh3[MAXN];
struct node{ int s,t,lca,len;}a[MAXN];
inline int getint(){
int w=,q=; char c=getchar(); while((c<''||c>'') && c!='-') c=getchar();
if(c=='-') q=,c=getchar(); while (c>=''&&c<='') w=w*+c-'',c=getchar(); return q?-w:w;
}
inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
inline void init(int x,int fa){ for(int i=first[x];i;i=next[i]) { int v=to[i]; if(v==fa) continue; deep[v]=deep[x]+; init(v,x); f[v][]=x; } }
inline int lca(int x,int y){
if(deep[x]<deep[y]) swap(x,y); int t=; while((<<t)<=deep[x]) t++; t--;
for(int i=t;i>=;i--) if(deep[x]-(<<i)>=deep[y]) x=f[x][i]; if(x==y) return y;
for(int i=t;i>=;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][];
} inline void dfs(int x,int fa){
int now=w[x]+deep[x],cun; if(now<=MAXD) cun=tong[now];
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
dfs(v,x);
}
tong[deep[x]]+=val[x]; if(now<=MAXD) ans[x]=tong[now]-cun;
for(int i=,ss=ljh[x].size();i<ss;i++) tong[deep[ljh[x][i]]]--;
} inline void DFS(int x,int fa){
int now=deep[x]-w[x],cun; now+=; cun=num[now];
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
DFS(v,x);
}
for(int i=,ss=ljh2[x].size();i<ss;i++) num[+ljh2[x][i]]++;
ans[x]+=num[now]-cun;
for(int i=,ss=ljh3[x].size();i<ss;i++) num[+ljh3[x][i]]--;
} inline void work(){
n=getint(); m=getint(); int x,y; for(int i=;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); }
for(int i=;i<=n;i++) w[i]=getint(); deep[]=; init(,); for(int i=;i<=n;i++) MAXD=max(MAXD,deep[i]);
for(int j=;j<=;j++) for(int i=;i<=n;i++) f[i][j]=f[f[i][j-]][j-];
for(int i=;i<=m;i++) {
a[i].s=getint(),a[i].t=getint(),val[a[i].s]++;
a[i].lca=lca(a[i].s,a[i].t),a[i].len=deep[a[i].s]+deep[a[i].t]-deep[a[i].lca]*;
ljh[a[i].lca].push_back(a[i].s);
}
dfs(,);
for(int i=;i<=m;i++) {
ljh2[a[i].t].push_back(deep[a[i].t]-a[i].len);
ljh3[a[i].lca].push_back(deep[a[i].t]-a[i].len);
}
DFS(,);
for(int i=;i<=m;i++) if(deep[a[i].s]-deep[a[i].lca]==w[a[i].lca]) ans[a[i].lca]--;
for(int i=;i<=n;i++) printf("%d ",ans[i]);
} int main()
{
work();
return ;
}
 
 
  $O(n+m):$
 
 //It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
using namespace std;
typedef long long LL;
const int MAXN = ;
const int MAXM = ;
int n,m,ecnt,first[MAXN],next[MAXM],to[MAXM],f[MAXN][],deep[MAXN],ans[MAXN],val[MAXN],tong[MAXN],MAXD,w[MAXN],num[];
int head[MAXN],tt[MAXM],nn[MAXM],father[MAXN],vis[MAXN];
vector<int>ljh[MAXN],ljh2[MAXN],ljh3[MAXN];
struct node{ int s,t,lca,len;}a[MAXN];
inline int getint(){
int w=,q=; char c=getchar(); while((c<''||c>'') && c!='-') c=getchar();
if(c=='-') q=,c=getchar(); while (c>=''&&c<='') w=w*+c-'',c=getchar(); return q?-w:w;
}
inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
inline void LINK(int x,int y){ nn[++ecnt]=head[x]; head[x]=ecnt; tt[ecnt]=y; }
inline int find(int x){ if(father[x]!=x) father[x]=find(father[x]); return father[x]; }
inline void init(int x,int fa){
father[x]=x; vis[x]=;
for(int i=head[x];i;i=nn[i]) {
int v=tt[i];
if(x==a[v].s&&vis[a[v].t]) a[v].lca=find(a[v].t);
if(x==a[v].t&&vis[a[v].s]) a[v].lca=find(a[v].s);
}
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
deep[v]=deep[x]+; init(v,x); father[v]=x;
f[v][]=x;
}
} inline int lca(int x,int y){
if(deep[x]<deep[y]) swap(x,y); int t=; while((<<t)<=deep[x]) t++; t--;
for(int i=t;i>=;i--) if(deep[x]-(<<i)>=deep[y]) x=f[x][i]; if(x==y) return y;
for(int i=t;i>=;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][];
} inline void dfs(int x,int fa){
int now=w[x]+deep[x],cun; if(now<=MAXD) cun=tong[now];
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
dfs(v,x);
}
tong[deep[x]]+=val[x]; if(now<=MAXD) ans[x]=tong[now]-cun;
for(int i=,ss=ljh[x].size();i<ss;i++) tong[deep[ljh[x][i]]]--;
} inline void DFS(int x,int fa){
int now=deep[x]-w[x],cun; now+=; cun=num[now];
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
DFS(v,x);
}
for(int i=,ss=ljh2[x].size();i<ss;i++) num[+ljh2[x][i]]++;
ans[x]+=num[now]-cun;
for(int i=,ss=ljh3[x].size();i<ss;i++) num[+ljh3[x][i]]--;
} inline void work(){
n=getint(); m=getint(); int x,y; for(int i=;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); }
for(int i=;i<=n;i++) w[i]=getint(); ecnt=;
for(int i=;i<=m;i++) { a[i].s=getint(),a[i].t=getint(),val[a[i].s]++; LINK(a[i].s,i); LINK(a[i].t,i);}
deep[]=; init(,); for(int i=;i<=n;i++) MAXD=max(MAXD,deep[i]);
for(int j=;j<=;j++) for(int i=;i<=n;i++) f[i][j]=f[f[i][j-]][j-];
for(int i=;i<=m;i++) {
a[i].len=deep[a[i].s]+deep[a[i].t]-deep[a[i].lca]*;
ljh[a[i].lca].push_back(a[i].s);
}
dfs(,);
for(int i=;i<=m;i++) {
ljh2[a[i].t].push_back(deep[a[i].t]-a[i].len);
ljh3[a[i].lca].push_back(deep[a[i].t]-a[i].len);
}
DFS(,);
for(int i=;i<=m;i++) if(deep[a[i].s]-deep[a[i].lca]==w[a[i].lca]) ans[a[i].lca]--;
for(int i=;i<=n;i++) printf("%d ",ans[i]);
} int main()
{
work();
return ;
}

UOJ261 【NOIP2016】天天爱跑步的更多相关文章

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

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

  2. [Noip2016]天天爱跑步 LCA+DFS

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

  3. 【LG1600】[NOIP2016]天天爱跑步

    [LG1600][NOIP2016]天天爱跑步 题面 洛谷 题解 考虑一条路径\(S\rightarrow T\)是如何给一个观测点\(x\)造成贡献的, 一种是从\(x\)的子树内出来,另外一种是从 ...

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

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

  5. BZOJ4719 [Noip2016]天天爱跑步

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000作者博客:http://www.cnblogs.com/ljh2000-jump/转 ...

  6. noip2016天天爱跑步

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

  7. bzoj 4719: [Noip2016]天天爱跑步

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

  8. NOIP2016 天天爱跑步 80分暴力

    https://www.luogu.org/problem/show?pid=1600 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养 ...

  9. 4719: [Noip2016]天天爱跑步

    Time Limit: 40 Sec Memory Limit: 512 MB Submit: 1986 Solved: 752 [Submit][Status][Discuss] Descripti ...

  10. NOIP2016 天天爱跑步 线段树合并_桶_思维题

    竟然独自想出来了,好开心 Code: #include<bits/stdc++.h> #define setIO(s) freopen(s".in","r&q ...

随机推荐

  1. Python3.1-标准库之Numpy

    这系列用来介绍Python的标准库的支持Numpy部分.资料来自http://wiki.scipy.org/Tentative_NumPy_Tutorial,页面有许多链接,这里是直接翻译,所以会无法 ...

  2. 谈谈关于Python里面小数点精度控制的问题

    基础 浮点数是用机器上浮点数的本机双精度(64 bit)表示的.提供大约17位的精度和范围从-308到308的指数.和C语言里面的double类型相同.Python不支持32bit的单精度浮点数.如果 ...

  3. Altera OpenCL用于计算机领域的13个经典案例(转)

    英文出自:Streamcomputing 转自:http://www.csdn.net/article/2013-10-29/2817319-the-application-areas-opencl- ...

  4. MPLS

    Multiprotocol Label Switching From Wikipedia, the free encyclopedia "MPLS" redirects here. ...

  5. C语言输入输出整数

    scanf("%llu", &x); printf("%llu\n", x); scanf("%u", &x); print ...

  6. python 2.7 简单模拟登陆网站

    举个栗子,首先创建网络会话, 然后就可以用创建的session来访问网页了. session.get(URL) #-*- coding:utf-8 -*- import requests import ...

  7. directly receive json data from javascript in mvc

    if you send json data to mvc,how can you receive them and parse them more simply? you can do it like ...

  8. Beta版本冲刺———第五天

    会议照片: 项目燃尽图: 1.项目进展: 困难:基本计划中增加的功能已经完成,但是在"如何保存每次游戏的分数,并将其排序列在排行榜中"遇到麻烦,现在小组都在一起协商攻克中.

  9. MySQL数据库my.cnf性能参数如何调优

    提供一个MySQL 5.6版本适合在1GB内存VPS上的my.cnf配置文件.配置文件可以到这里下载:: 下载my.cnf [client] port = 3306 socket = /tmp/mys ...

  10. 区间DP lightoj 1422

    t个样例 n  n个数字 从 1->n  穿衣服  脱了就不能再用 ,可以套 问最少几件衣服 #include<stdio.h> #include<string.h> # ...