题目描述

小c同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。

这个游戏的地图可以看作一一棵包含 个结点和 条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从的连续正整数。

现在有个玩家,第个玩家的起点为 ,终点为 。每天打卡任务开始时,所有玩家在第秒同时从自己的起点出发, 以每秒跑一条边的速度, 不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务。 (由于地图是一棵树, 所以每个人的路径是唯一的)

小C想知道游戏的活跃度, 所以在每个结点上都放置了一个观察员。 在结点的观察员会选择在第秒观察玩家, 一个玩家能被这个观察员观察到当且仅当该玩家在第秒也理到达了结点 。 小C想知道每个观察员会观察到多少人?

注意: 我们认为一个玩家到达自己的终点后该玩家就会结束游戏, 他不能等待一 段时间后再被观察员观察到。 即对于把结点作为终点的玩家: 若他在第秒重到达终点,则在结点的观察员不能观察到该玩家;若他正好在第秒到达终点,则在结点的观察员可以观察到这个玩家。

输入输出格式

输入格式:

第一行有两个整数 。其中代表树的结点数量, 同时也是观察员的数量, 代表玩家的数量。

接下来 行每行两个整数,表示结点 到结点 有一条边。

接下来一行 个整数,其中第个整数为 , 表示结点出现观察员的时间。

接下来 行,每行两个整数,和,表示一个玩家的起点和终点。

对于所有的数据,保证

输出格式:

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

输入输出样例

输入样例#1:

6 3
2 3
1 2
1 4
4 5
4 6
0 2 5 1 2 3
1 5
1 3
2 6
输出样例#1:

2 0 0 1 1 1 
输入样例#2:

5 3
1 2
2 3
2 4
1 5
0 1 0 3 0
3 1
1 4
5 5
输出样例#2:

1 2 1 0 1 

说明

【样例1说明】

对于1号点,,故只有起点为1号点的玩家才会被观察到,所以玩家1和玩家2被观察到,共有2人被观察到。

对于2号点,没有玩家在第2秒时在此结点,共0人被观察到。

对于3号点,没有玩家在第5秒时在此结点,共0人被观察到。

对于4号点,玩家1被观察到,共1人被观察到。

对于5号点,玩家1被观察到,共1人被观察到。

对于6号点,玩家3被观察到,共1人被观察到。

【子任务】

每个测试点的数据规模及特点如下表所示。 提示: 数据范围的个位上的数字可以帮助判断是哪一种数据类型。

【提示】

如果你的程序需要用到较大的栈空问 (这通常意味着需要较深层数的递归), 请务必仔细阅读选手日录下的文本当rumung:/stact.p″, 以了解在最终评测时栈空问的限制与在当前工作环境下调整栈空问限制的方法。

这道题的25分可以直接暴力得到。

对于15分链状的情况,我们可以设一个不定长数组对于每个点的观察员观察时间t在所在点向左t点打标记,向右t点打标记即可。然后对于每组起点与终点,查看起点不定长数组的所在位置是否在起点和终点之间。

对于20分起点为1的情况,以1为根,所有经过时间一定是深度,所以做一个桶即可。

对于20分终点为1的情况,同样以1为根,每个点i能观察到的路径起点u一定保证dep[u]=dep[i]-time[i];time[i]表示观察时间首先做一个桶,将起点放入,之后设一个关于深度的桶,对于每个点i的答案只需知道cnt[dep[u]+time[i]]在遍历前后的变化值即可。

80分代码:

#include<cstdio>
#include<vector>
using namespace std;
const int N=;
struct X
{
int v,f,n;
}x[N<<];
int dep[N],s,fa[N],ti[N],tong[N];
vector<int>lian[N];
void add(int u,int v)
{
x[++s].n=x[u].f;
x[x[u].f=s].v=v;
}
void dfs(int u)
{
for(int i=x[u].f;i;i=x[i].n)
if(x[i].v!=fa[u])
dep[x[i].v]=dep[fa[x[i].v]=u]+,dfs(x[i].v);
}
int lca(int u,int v)
{
while(u!=v)
if(dep[u]>=dep[v]) u=fa[u];
else v=fa[v];
return u;
}
void ju(int u,int v,int tv)
{
int tu=;
if(!ti[u]) tong[u]++;
if(u!=v&&ti[v]==tv) tong[v]++;
while(u!=v)
if(dep[u]>=dep[v])
{
u=fa[u];
++tu;
if(tu==ti[u]&&u!=v) ++tong[u];
}
else
{
v=fa[v];
--tv;
if(tv==ti[v]&&u!=v) ++tong[v];
}
return;
}
void dfs2(int u)
{
for(int i=x[u].f;i;i=x[i].n)
if(x[i].v!=fa[u])
fa[x[i].v]=u,dep[x[i].v]=dep[u]+,dfs2(x[i].v),tong[u]+=tong[x[i].v];
}
int main()
{
//freopen("running.in","r",stdin),freopen("running.out","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
for(int i=;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
for(int i=;i<=n;i++) scanf("%d",&ti[i]);
if(n==)
{
for(int i=;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
++tong[v];
}
dfs2();
for(int i=;i<=n;i++)
if(dep[i]==ti[i]) printf("%d ",tong[i]);
else printf("0 ");
}
else if(n==)
{
for(int i=;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
lian[u].push_back(v);
}
for(int i=;i<=n;i++)
{
int ans=;
if(ti[i]<i)
{
int len=lian[i-ti[i]].size();
for(int j=;j<len;j++)
if(lian[i-ti[i]][j]>=i) ans++;
}
if(ti[i]+i<=n)
{
int len=lian[ti[i]+i].size();
for(int j=;j<len;j++)
if(lian[ti[i]+i][j]<i||(lian[ti[i]+i][j]==i&&ti[i])) ans++;
}
printf("%d ",ans);
}
}
else if(n<=)
{
dfs();
for(int i=;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
int t=lca(u,v),dis=dep[u]+dep[v]-*dep[t];
ju(u,v,dis);
}
for(int i=;i<=n;i++)
printf("%d ",tong[i]);
}
return ;
}

100分:

链上算法都有了,树上算法还远吗?

我们把每条路径拆成从u到lca和lca到v的两条路径。(对于原本就是链的路径特判,并且非链的lca可能会多算要减去1)

1.统计u到lca的路径:与上文中以一为终点的方法相似,对于一个点a它所能观察到的点的深度应为dep[a]+time[a],所以我们同样用桶作记录。对于一条路径我们在离开u点时给dep[u]++,

离开lca时给dep[u]--;

2.统计lca到v的路径时,只是将所改的深度变为dep[lca]*2-dep[u],因为这个值可能小于0所以加上最大值。

#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N=;
struct X
{
int v,f,n;
}x[N<<],y[N<<];
vector<int>ad1[N],ad2[N],de1[N],de2[N];
int dep[N],s,ti[N],to1[N],to2[N<<],n,ans[N],lc[N];
void add(int u,int v,X *t)
{
t[++s].n=t[u].f;
t[t[u].f=s].v=v;
}
int find(int a)
{
int b=a;
while(to1[b]) b=to1[b];
while(b!=a)
{
int t=to1[a];
to1[a]=b;
a=t;
}
return b;
}
void dfs1(int u,int fa)
{
for(int i=x[u].f;i;i=x[i].n)
if(x[i].v!=fa)
{
dep[x[i].v]=dep[u]+;
dfs1(x[i].v,u);
to1[x[i].v]=u;
}
for(int i=y[u].f;i;i=y[i].n)
if(y[i].v==||dep[y[i].v]) lc[i>>]=find(y[i].v);
}
void dfs2(int u,int fa)
{ if(dep[u]+ti[u]<=n) ans[u]-=to1[dep[u]+ti[u]];
if(ti[u]<=n) ans[u]-=to2[N+dep[u]-ti[u]];
for(int i=x[u].f;i;i=x[i].n)
if(x[i].v!=fa) dfs2(x[i].v,u);
for(int i=;i<ad2[u].size();i++) ++to2[ad2[u][i]];
if(ti[u]<=n) ans[u]+=to2[N+dep[u]-ti[u]];
for(int i=;i<de2[u].size();i++) --to2[de2[u][i]];
for(int i=;i<ad1[u].size();++i) ++to1[ad1[u][i]];
if(dep[u]+ti[u]<=n) ans[u]+=to1[dep[u]+ti[u]];
for(int i=;i<de1[u].size();++i) --to1[de1[u][i]];
}
int main()
{
int m;
scanf("%d%d",&n,&m);
for(int i=;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v,x);add(v,u,x);
}
for(int i=;i<=n;++i) scanf("%d",&ti[i]);
s=;
for(int i=;i<=m;++i)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v,y);add(v,u,y);
}
dfs1(,);
memset(to1,,sizeof(to1));
for(int i=;i<=m;++i)
{
int u=y[i<<|].v,v=y[i<<].v;
if(lc[i]==v)
{
ad1[u].push_back(dep[u]);
de1[v].push_back(dep[u]);
}
else if(lc[i]==u)
{
de2[u].push_back(dep[u]+N);
ad2[v].push_back(dep[u]+N);
}
else
{
ad1[u].push_back(dep[u]);
de1[lc[i]].push_back(dep[u]);
de2[lc[i]].push_back((dep[lc[i]]<<)-dep[u]+N);
ad2[v].push_back((dep[lc[i]]<<)-dep[u]+N);
if(dep[lc[i]]+ti[lc[i]]==dep[u]) --ans[lc[i]];
}
}
dfs2(,);
for(int i=;i<=n;i++)
printf("%d ",ans[i]);
return ;
}

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. bzoj 4719: [Noip2016]天天爱跑步

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

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

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

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

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

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

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

随机推荐

  1. 【百度地图开发之二】基于Fragment的地图框架的使用

    写在前面的话: [百度地图开发之二]基于Fragment的地图框架的使用(博客地址:http://blog.csdn.net/developer_jiangqq),转载请注明. Author:hmji ...

  2. Chapter 1 First Sight——2

    "Bella," my mom said to me — the last of a thousand times — before I got on the plane. &qu ...

  3. js中的全局函数

    以前没搞懂JS的全局函数,全局函数和window对象的函数不一样.全局函数不属于任何一个内置对象. JS包含以下7个全局函数,用于一些常用的功能:escape(),eval(),isNan(),isF ...

  4. spring和springMVC的上下文

    上下文可以替代注解, 但是注解更方便 package com.tgb.web.controller; import javax.annotation.Resource; import javax.se ...

  5. php 缓存之 APC 和apcu

    php opcode 缓存 apc. 其实,我自己的理解, php apc 缓存其实分两部分, 一部分是 缓存 类似于 java 编译的中间的 字节码, 不同于c 语言编译之后的二进制的机器码. ph ...

  6. PAT (Advanced Level) 1084. Broken Keyboard (20)

    简单题. #include<cstdio> #include<cstring> #include<cmath> #include<vector> #in ...

  7. JQuery收集

  8. arrayList LinkedList HashMap HashTable的区别

    ArrayList 采用的是数组形式来保存对象的,这种方式将对象放在连续的位置中,所以最大的缺点就是插入删除时非常麻烦 LinkedList 采用的将对象存放在独立的空间中,而且在每个空间中还保存下一 ...

  9. servlet与CGI的区别

    与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以 ...

  10. SQL TOP分页

    SQL TOP分页 2010-11-12 16:35:29|  分类: SQL |  标签: |字号大中小 订阅     1.分页方案一:(利用Not In和SELECT TOP分页) 语句形式:   ...