【BzoJ 4719】【Noip2016】【天天爱跑步】【lca】【方程移项】【桶排优化】

来源:互联网 发布:hecras软件 编辑:程序博客网 时间:2024/05/20 03:38


4719: [Noip2016]天天爱跑步

Time Limit: 40 Sec  Memory Limit: 512 MB
Submit: 1337  Solved: 451
[Submit][Status][Discuss]

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

2 0 0 1 1 1

HINT


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

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

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

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

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

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

Source

[Submit][Status][Discuss]


HOME Back

想到去年考noip时,当时写了一个暴力bfs就觉得是正解还真是naive呢;

本体由于n和m都会很大,所以如果是每个路径都暴力跑一次保证TLE没的说;

那么若想AC,肯定是经过预处理后直接求值,而且复杂度绝对不会超过 O(n*log(n));

那么我们细看一个观察员若是正好看到某个人跑过的时候需要满足的性质:
【1】deep[S] - deep[i] = t[i]

【2】lon - (deep[E] - deep[i]) = t[i] (S为一个跑步者的起点,E为终点;t[i]为站在点i上的观察员看人的时间)

那么就可以推导出式子

【1】deep[S] = t[i] +deep[i]

【2】lon - deep[E] = t[i] - deep[i]

那么我们不就可以发现一个点上可以看到的人数可以用两个桶表示;

那么怎么合并桶嘞?

我们发现经过一个点时,会有一些S或E使桶中的一些元素值增加,也会有一些LCA(S , E)(即一个路径不会再被经过时)使得一些元素值减少,所以我们可以不用去想合并桶,而是便利点时顺带加减(此处运用的是差分的思想)




那么在此整理总结一下:

本题由于如果是便利每条路径绝对会TLE,所以使我们想到可能是需要集中在一起处理;

由于推出式子:

【1】deep[S] - deep[i] = t[i]

【2】lon - (deep[E] - deep[i]) = t[i] 

然后由于我们看到这个等式同测的未知数并不统一,但是可以通过移向使其变得统一,所以得到等式:

【1】deep[S] = t[i] +deep[i](针对S -> LCA 路径上的点)

【2】lon - deep[E] = t[i] - deep[i](针对S -> LCA 路径上的点)

然后发现一个点上的观察员可以看到的跑步者都会有上述特性的其中一个,便想使用两个桶(一个针对S,一个针对E)将其储存起来(于是利用到了查分);

那么一个点的答案便是【桶1】[t[i] + deep[i]] +【桶2】[t[i] - deep[i] +3 * 10 ^5](由于是相减可能出现负数,所以需要将等式2左右两边同时加一个3 * 10 ^5以保证其肯定不为负,变为等式:lon - deep[E] + 3 * 10 ^5 = t[i] - deep[i] + 3 * 10 ^5)




注意:

(1)本题还有一个坑点在于对于一个路径的lca来说他同时在S -> LCA 和E -> LCA两条路径上,所以计算时会算重复,此处需要通过控制什么时候增加桶值什么时候减少桶值来解决此类问题

(2)虽然上面说了:答案便是【桶1】[t[i] + deep[i]] +【桶2】[t[i] - deep[i] +3 * 10 ^5]‘;但是因为算法实现时并非合并桶,所以当前的桶并不是你为根的子树的值,需要减去之前其他树上算出的值



代码如下:

#include<vector>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#define M 300001using namespace std;int n , m , w[M] , ans[M];int ind[M] , nex[M * 2] , e[M * 2] ,cnt;int st_up[M] , st_do[M * 2] , dip[M] , st[M][21];vector<int> u_add[M] , u_div[M] , d_add[M] , d_div[M];void add(int a , int b){nex[++cnt] = ind[a];e[ind[a] = cnt] = b;}void Init(int v , int f){dip[v] = dip[f] + 1;st[v][0] = f;for(int i = 1 ; i <= 20 ; ++i)st[v][i] = st[st[v][i - 1]][i - 1];for(int i = ind[v] ; i ; i = nex[i])if(e[i] != f)Init(e[i] , v);}int get_lca(int a , int b){if(dip[a] < dip[b])swap(a , b);int k = dip[a] - dip[b];for(int i = 20 ; i >= 0 ; --i)if((1 << i) <= k){a = st[a][i];k -= (1 << i);}if(a == b)return a;for(int i = 20 ; i >= 0 ; --i)if(st[a][i] != st[b][i]){a = st[a][i];b = st[b][i];}return st[a][0];}void dfs(int v , int f){int x = st_up[dip[v] + w[v]] , y = st_do[w[v] - dip[v] + M];for(int i = ind[v] ; i ; i = nex[i])if(e[i] != f)dfs(e[i] , v);for(int i = 0 ; i < u_add[v].size() ; ++i)st_up[u_add[v][i]]++;for(int i = 0 ; i < d_add[v].size() ; ++i)st_do[d_add[v][i]]++;for(int i = 0 ; i < u_div[v].size() ; ++i)st_up[u_div[v][i]]--;ans[v] = st_up[dip[v] + w[v]] + st_do[w[v] - dip[v] + M] - x - y;for(int i = 0 ; i < d_div[v].size() ; ++i)st_do[d_div[v][i]]--;}int main(){scanf("%d%d",&n,&m);for(int i = 1 ; i < n ; ++i){int a , b;scanf("%d%d",&a,&b);add(a , b);add(b , a);}Init(1 , 0);for(int i = 1 ; i <= n ; ++i)scanf("%d",&w[i]);for(int i = 1 ; i <= m ; ++i){int st , ed;scanf("%d%d",&st,&ed);int lca = get_lca(st , ed);int lon = dip[st] + dip[ed] - dip[lca] * 2;u_add[st].push_back(dip[st]);u_div[lca].push_back(dip[st]);d_add[ed].push_back(lon - dip[ed] + M);d_div[lca].push_back(lon - dip[ed] + M);}dfs(1 , 0);for(int i = 1 ; i <= n ; ++i)printf("%d ",ans[i]);}/*6 32 31 2 1 4 4 5 4 6 0 2 5 1 2 3 1 5 1 3 2 6*/



阅读全文
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 不小心提交了两次中信信用卡怎么办 淘宝买东西扣了银行卡两次钱怎么办 拼多多同一个订单支付了两次怎么办 京东手机号服务密码忘记了怎么办 京东买的东西转手给别人保修怎么办 改了无线网密码还是上不去网怎么办 买的京东e卡丢了怎么办 已认证未抵扣的发票发生退货怎么办 办的消费卡不给退怎么办 银行卡密码忘了手机号也换了怎么办 公务卡在当当购物的刷卡单怎么办 杭州市网签提示住宅均价异常怎么办 吃鸡账号没有绑定手机就买了怎么办 微信绑定的银行卡密码错误怎么办 淘宝单张券已领取达上限怎么办 去办公室给领导送礼总有人怎么办 招行信用卡临时额度到期后怎么办 中银e令不想用了怎么办 中银e令密码忘了怎么办 银行卡预留手机号改了支付宝怎么办 手机一直收到支付验证码短信怎么办 微信绑定银行卡手机号码换了怎么办 银行卡网银登录输错密码锁了怎么办 中行信用卡主付卡的付卡怎么办停 在苏宁网购的移动空调要退货怎么办 单位发的购物卡掉了怎么办 支付宝ofo退押金后余额怎么办 e招贷分期多还了怎么办 别人用我的手机号贷款不还怎么办 电脑文件夹怎么设密码忘记了怎么办 苹果手机id有分机和主机怎么办 绝地求生号被盗了邮箱被改了怎么办 微信邮箱怎么接收不到验证码怎么办 要申请一个特定的qq邮箱号怎么办 中银e贷额度为0怎么办 中银e贷被冻结了怎么办 中国银行e贷款填错了被拒怎么办 中银e贷逾期2天怎么办 大学生助学贷款网站密码忘了怎么办 乳晕毛囊挤压捏起来有小硬节怎么办 手机版模拟人生孕妇任务卡死怎么办