博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
luogu1600 天天爱跑步
阅读量:5132 次
发布时间:2019-06-13

本文共 5109 字,大约阅读时间需要 17 分钟。

题目大意

  游戏的地图可以看作一一棵包含 N个结点和N-1 条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从1到N的连续正整数。现在有个玩家,第个玩家的起点为Si ,终点为Ti  。每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发, 以每秒跑一条边的速度,不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务。每个结点上都放置了一个观察员。 在结点的观察员会选择在第Wj秒观察玩家, 一个玩家能被这个观察员观察到当且仅当该玩家在第Wj秒也理到达了结点J  。 小C想知道每个观察员会观察到多少人?$n,m\leq 300000$

题解

  凡是跟树有关的查询等问题都要将无根树化为有根树,而此题中我们可以把所有的路径$(s,t)$分为两个部分:$s\rightarrow \mathrm{lca}(s,t), \mathrm{lca}(s,t)\rightarrow t$。我们先统计每个点观察到的上升玩家的个数,再统计每个点观察到的下降玩家的个数。如果玩家(s,t)在上升的时候遇到了点cur,则cur->Depth + cur->W == s->Depth(1);如果是在下降的时候遇到的点cur,则cur->W - cur->Depth == dist(s,t) - t->Depth(2)。而(s,t)在上升时遇到cur当且仅当在Dfs时lca(s,t)仍然在搜索栈内。因此我们可以维护一个桶,分别维护当lca(s,t)在搜索栈中时,(1)和(2)中满足等号右边的值的路径的个数。在回溯时1.更新当前节点Ans其观察到的玩家的数量为搜索子树后桶内的值减去搜索字数前桶内的值(也就是子树内的值)。2.将以当前节点为lca的路径在桶内的贡献减去。统计上升节点时,先1后2;统计下降节点时,先2后1,以防止lca算重的情况。

  注意求lca时,特判路径起终点相同的情况。

#include 
#include
#include
#include
using namespace std;const int MAX_NODE = 300010, MAX_EDGE = MAX_NODE * 2, MAX_PATH = 300010, MAX_LINK = MAX_PATH * 4, MAX_W = MAX_NODE;int Bucket[MAX_W * 2];struct Node;struct Edge;struct Link;struct Path;struct Node{ Edge *Head; Link *AsLcaHead, *AsTHead, *TarjanHead; Node *Father, *UnFa; int Depth, W, DfsN, AsSCnt, Ans;}_nodes[MAX_NODE], *Root;int TotNode;struct Edge{ Node *To; Edge *Next;}_edges[MAX_EDGE];int _eCount;struct Link{ Path *In; Link *Next;}_links[MAX_LINK];int _linkCnt;struct Path{ Node *Start, *Target, *Lca; int Dist;}_paths[MAX_PATH];int TotPath;void AddEdge(Node *from, Node *to){ Edge *e = _edges + ++_eCount; e->To = to; e->Next = from->Head; from->Head = e;}void AddLink(Link*&head, Path *in){ Link *link = _links + ++_linkCnt; link->In = in; link->Next = head; head = link;}Node *GetRoot(Node *cur){ return cur->UnFa == cur ? cur : cur->UnFa = GetRoot(cur->UnFa);}void Tarjan_Dfs(Node *cur, Node *fa, int depth){ cur->Father = fa; cur->DfsN = 1; cur->Depth = depth; for (Edge *e = cur->Head; e; e = e->Next) { if (e->To == cur->Father) continue; Tarjan_Dfs(e->To, cur, depth + 1); e->To->UnFa = cur; } cur->DfsN = 2; for (Link *link = cur->TarjanHead; link; link = link->Next) { Node *other = link->In->Start == cur ? link->In->Target : link->In->Start; if (other->DfsN != 2) continue; link->In->Lca = GetRoot(other); link->In->Dist = cur->Depth + other->Depth - link->In->Lca->Depth * 2; AddLink(link->In->Lca->AsLcaHead, link->In); }}void GetLca(){ for (int i = 1; i <= TotPath; i++) { AddLink(_paths[i].Start->TarjanHead, _paths + i); if (_paths[i].Start == _paths[i].Target) continue; AddLink(_paths[i].Target->TarjanHead, _paths + i); } for (int i = 1; i <= TotNode; i++) _nodes[i].UnFa = _nodes + i; Tarjan_Dfs(Root, NULL, 1);}void Dfs1(Node *cur, int *sDepthCnt){ int prev = sDepthCnt[cur->Depth + cur->W]; sDepthCnt[cur->Depth] += cur->AsSCnt; for (Edge *e = cur->Head; e; e = e->Next) { if (e->To == cur->Father) continue; Dfs1(e->To, sDepthCnt); } cur->Ans += sDepthCnt[cur->Depth + cur->W] - prev; for (Link *link = cur->AsLcaHead; link; link = link->Next) sDepthCnt[link->In->Start->Depth]--;}void GetRunUp(){ for (int i = 1; i <= TotPath; i++) _paths[i].Start->AsSCnt++; memset(Bucket, 0, sizeof(Bucket)); Dfs1(Root, Bucket);}void Dfs2(Node *cur, int *bucket){#define cnt(x) bucket[x + TotNode] int prev = cnt(cur->Depth - cur->W); for (Link *link = cur->AsTHead; link; link = link->Next) cnt(cur->Depth - link->In->Dist)++; for (Edge *e = cur->Head; e; e = e->Next) { if (e->To == cur->Father) continue; Dfs2(e->To, bucket); } for (Link *link = cur->AsLcaHead; link; link = link->Next) { cnt(link->In->Target->Depth - link->In->Dist)--; assert(cnt(link->In->Target->Depth - link->In->Dist) >= 0); } cur->Ans += cnt(cur->Depth - cur->W) - prev;}void GetRunDown(){ for (int i = 1; i <= TotPath; i++) AddLink(_paths[i].Target->AsTHead, _paths + i); memset(Bucket, 0, sizeof(Bucket)); Dfs2(Root, Bucket);}void Read(){ Root = _nodes + 1; scanf("%d%d", &TotNode, &TotPath); for (int i = 2; i <= TotNode; i++) { int u, v; scanf("%d%d", &u, &v); AddEdge(_nodes + u, _nodes + v); AddEdge(_nodes + v, _nodes + u); } for (int i = 1; i <= TotNode; i++) scanf("%d", &_nodes[i].W); for (int i = 1; i <= TotPath; i++) { int s, t; scanf("%d%d", &s, &t); _paths[i].Start = _nodes + s; _paths[i].Target = _nodes + t; }}void Write(){ bool isFirst = true; for (int i = 1; i <= TotNode; i++) { if (!isFirst) putchar(' '); isFirst = false; printf("%d", _nodes[i].Ans); } printf("\n");}int main(){ Read(); GetLca(); GetRunUp(); GetRunDown(); Write(); return 0;}

  

转载于:https://www.cnblogs.com/headboy2002/p/9784675.html

你可能感兴趣的文章
SIP服务器性能测试工具SIPp使用指导(转)
查看>>
Vue_(组件通讯)子组件向父组件传值
查看>>
STM32单片机使用注意事项
查看>>
032. asp.netWeb用户控件之一初识用户控件并为其自定义属性
查看>>
移动开发平台-应用之星app制作教程
查看>>
leetcode 459. 重复的子字符串(Repeated Substring Pattern)
查看>>
springboot No Identifier specified for entity的解决办法
查看>>
浅谈 unix, linux, ios, android 区别和联系
查看>>
51nod 1428 活动安排问题 (贪心+优先队列)
查看>>
latex for wordpress(一)
查看>>
如何在maven工程中加载oracle驱动
查看>>
Flask 系列之 SQLAlchemy
查看>>
aboutMe
查看>>
【Debug】IAR在线调试时报错,Warning: Stack pointer is setup to incorrect alignmentStack,芯片使用STM32F103ZET6...
查看>>
一句话说清分布式锁,进程锁,线程锁
查看>>
FastDFS使用
查看>>
服务器解析请求的基本原理
查看>>
[HDU3683 Gomoku]
查看>>
下一代操作系统与软件
查看>>
[NOIP2013提高组] CODEVS 3287 火车运输(MST+LCA)
查看>>