【NOIP2016】DAY1 T2 天天爱跑步
【NOIP2016】DAY1 T2 天天爱跑步
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
Solution
这道题目吧,好难……
首先,我们读入的是一棵树,在某些结点上有一些玩家(废话,读过题的都知道)
那么对于某一个玩家, 其实在树的某条链上有两种情况,一种是从下向上,另一种是从上向下,即该玩家还没走到起点和终点的lca,另一种已经过了起点和终点的lca(我废话真多)
我们先对于其中一种情况讨论,当该点从下向上时(我不会说是因为这种情况比较简单),如图P1,我们可以看出当且仅当w[i]+d[i]=d[x]时,从x出发的玩家才能被在i点的观察员观察到(d[i]表示结点i的深度),那么当该点从上向下时呢?我们看P2,显然当且仅当d[y]-d[i]=dis(x,y)-w[i](dis(x,y)表示x与y之间的距离)我们把这个式子变形一下,w[i]-d[i]=dis(x,y)-d[y](当然这个式子也可以写成w[i]-d[i]=d[x]-2*d[lca(x,y)],因为dis(x,y)=d[x]+d[y]-2*d[lca(x,y)])
P1 P2
现在我们遇上了一个大问题,这两个式子如何来实现?
利用一个高档货,树上差分(不知道哪里高档,反正就是高档就对了),我们便可以在x上+1,在lca(x,y)上-1,对于y也相同,在y上+1,在lca(x,y)上-1,当我们对于这棵树dfs的时候,遍历到i时,利用差分就可以做到求出在i的子树中的能走到i的玩家数
可是我们要求的不仅仅是玩家经过该点,还需在同一时刻出现,其实我们只要做两个桶就可以了,桶p[k]记录起点的d[x]=k的玩家数量,桶q[k]记录终点的dis(x,y)-d[y]=k的玩家数量,对于每个点的答案就是q[d[i]+w[i]]+p[w[i]-d[i]]-这个点遍历前q[d[i]+w[i]]+p[w[i]-d[i]](因为有些玩家从该点的祖先开始或结束但还没走到lca),当然我们需要记录以某个结点开始的玩家和以某个结束的玩家(这句话别看,因为我看了也懵,看下下面程序再结合一下这句话大概应该可能就可以看懂了吧)
还有一个小问题,就是当w[lca(x,y)]+d[lca(x,y)]==d[x]时,我们会发现向上走和向下走都会对lca有贡献,所以当lca可以观测到时我们需要把lca处的答案-1
但是还有一个坑,就是w[i]-d[i]可能小于0,那就把桶里面的数都加300000就可以了
讲得好水,看下程序吧(虽然说很丑)
#include<cstdio>
#include<utility>
struct r{
int to,last,w;
}e[],in1[],ou1[],in2[],ou2[];
int num=,num1=,num2=,num3=,num4=,head[],head1[],head2[],
head3[],head4[],w[],d[],f[][],ans[];
void add(int u,int v){
e[num].to=v;
e[num].last=head[u];
head[u]=num++;
}
void add_edge(int u,r &p,int*q,int&num,int w){
p.w=w;
p.last=q[u];
q[u]=num++;
}
inline int readin(){
int c=getchar(),ret=;
while (c<''||c>'') c=getchar();
while (c>=''&&c<='') ret=(ret<<)+(ret<<)+c-'',c=getchar();
return ret;
}
void dfs(int k,int fa){
d[k]=d[fa]+;
f[k][]=fa;
for (int i=;i<=;i++) f[k][i]=f[f[k][i-]][i-];
for (int i=head[k];i;i=e[i].last)if (e[i].to!=fa){
dfs(e[i].to,k);
}
}
int lca(int u,int v){
if (d[u]>d[v]) std::swap(u,v);
int y=d[v]-d[u];
for (int i=;i>=;i--) if ((y&(<<i))) v=f[v][i];
if (u==v) return u;
for (int i=;i>=;i--) if (f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
return f[u][];
}
int q[],p[];
void dfs2(int k,int fa){
int req=q[d[k]+w[k]],rep=p[w[k]-d[k]+];
for (int i=head1[k];i;i=in1[i].last) q[in1[i].w]++;
for (int i=head3[k];i;i=in2[i].last) p[in2[i].w]++;
for (int i=head[k];i;i=e[i].last) if (e[i].to!=fa) dfs2(e[i].to,k);
ans[k]+=q[d[k]+w[k]]-req+p[w[k]-d[k]+]-rep;
for (int i=head2[k];i;i=ou1[i].last) q[ou1[i].w]--;
for (int i=head4[k];i;i=ou2[i].last) p[ou2[i].w]--;
}
int main()
{
int n=readin(),m=readin(),u,v,x,y;
for (int i=;i<n;i++) u=readin(),v=readin(),add(u,v),add(v,u);
num=;
dfs(,);
for (int i=;i<=n;i++) w[i]=readin();
for (int i=;i<=m;i++){
x=readin(),y=readin();
int z=lca(x,y);
if (d[x]==d[z]+w[z]) ans[z]--;
add_edge(x,in1[num1],head1,num1,d[x]),
add_edge(z,ou1[num2],head2,num2,d[x]),
add_edge(y,in2[num3],head3,num3,d[x]-d[z]*+),
add_edge(z,ou2[num4],head4,num4,d[x]-d[z]*+);
}
dfs2(,);
for (int i=;i<=n;i++)
printf("%d ",ans[i]);
return ;
}
【NOIP2016】DAY1 T2 天天爱跑步的更多相关文章
- [NOIP2016 DAY1 T2]天天爱跑步-[差分+线段树合并][解题报告]
[NOIP2016 DAY1 T2]天天爱跑步 题面: B[NOIP2016 DAY1]天天爱跑步 时间限制 : - MS 空间限制 : 565536 KB 评测说明 : 2s Description ...
- Luogu P1600[NOIP2016]day1 T2天天爱跑步
号称是noip2016最恶心的题 基本上用了一天来搞明白+给sy讲明白(可能还没讲明白 具体思路是真的不想写了(快吐了 如果要看,参见洛谷P1600 天天爱跑步--题解 虽然这样不好但我真的不想写了 ...
- NOIP2016 DAY1 T2天天爱跑步
传送门 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 ...
- NOIP2016 Day1 T2 天天爱跑步(树上差分,LCA)
原文链接 原题链接 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏 ...
- 【NOIP 2016】Day1 T2 天天爱跑步
Problem Description 小 C 同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任 ...
- 【NOIP2016 Day1 T2】天天爱跑步
题目传送门:https://www.luogu.org/problemnew/show/P1600 感觉这两天在处理边界问题上有点神志不清......为了从80的暴力变成100,花了整整一个下午+一个 ...
- [NOIp2016提高组]天天爱跑步
题目大意: 有一棵n个点的树,每个点上有一个摄像头会在第w[i]秒拍照. 有m个人再树上跑,第i个人沿着s[i]到t[i]的路径跑,每秒钟跑一条边. 跑到t[i]的下一秒,人就会消失. 问每个摄像头会 ...
- P1600 [NOIP2016 提高组] 天天爱跑步 (树上差分)
对于一条路径,s-t,位于该路径上的观察员能观察到运动员当且仅当以下两种情况成立:(d[ ]表示节点深度) 1.观察员x在s-lca(s,t)上时,满足d[s]=d[x]+w[x]就能观察到,所以我们 ...
- [luogu]P1600 天天爱跑步[LCA]
[luogu]P1600 [NOIP 2016]天天爱跑步 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上 ...
随机推荐
- Python selenium chrome打包exe后禁用控制台输出滚动日志
Python selenium chrome打包exe后,在运行的过程中,如果遇到需要input()输入时,会发现被不断滚动刷新的日志把命令行输入快速顶掉了,通过查阅资料不断实践,发现以下方法有效: ...
- java8 新特性 lambda过滤
1. 定义实体类 package com.atguigu.java8; public class Employee { private int id; private String name; pri ...
- 【00】angular学习网站
[00] 学习资料: http://angularjs.cn/ 英文API:http://docs.angularjs.cn/api 中文API;http://www.apjs.n ...
- 【Codeforces 140C】New Year Snowmen
[链接] 我是链接,点我呀:) [题意] 题意 [题解] 每次都选择剩余个数最多的3个不同数字组成一组. 优先消耗剩余个数多的数字 这样能尽量让剩余的数字总数比较多,从而更加可能得到更多的3个组合 [ ...
- [luoguP3047] [USACO12FEB]附近的牛Nearby Cows(DP)
传送门 dp[i][j][0] 表示点 i 在以 i 为根的子树中范围为 j 的解 dp[i][j][1] 表示点 i 在除去 以 i 为根的子树中范围为 j 的解 状态转移就很好写了 ——代码 #i ...
- noip模拟赛 蒜头君打地鼠
分析:直接一个一个地去暴力枚举分数比较少,我们需要一种比较快的统计一定空间内1的数量,标准做法是前缀和,但是二维前缀和维护的是一个矩形内的值,这个是旋转过的该怎么办?可以把图旋转45°,不过这样比较考 ...
- Quartz原理解密
Quartz原理解密 Author: Dorae Date:2018年7月17日15:55:02 转载请注明出处 一.quartz概述 quartz是一个用java实现的开源任务调度框架,可以用来创建 ...
- 22、Java并发性和多线程-Java中的读/写锁
以下内容转自http://ifeve.com/read-write-locks/: 相比Java中的锁(Locks in Java)里Lock实现,读写锁更复杂一些.假设你的程序中涉及到对一些共享资源 ...
- PHP array_multisort()
定义和用法 array_multisort() 函数对多个数组或多维数组进行排序. 参数中的数组被当成一个表的列并以行来进行排序 - 这类似 SQL 的 ORDER BY 子句的功能.第一个数组是要排 ...
- 2 instances of postgresql but I really need one [closed]
I happen to have 2 installed instances of postgresql at my machine: 9.1 and 9.2: sudo service postgr ...