UOJ 261 [NOIP2016 DAY1 T2] 浅谈树上路径统计问题桶解法
2017-09-12 17:55
344 查看
世界真的很大
去年考NOIP的时候还是一个萌新(现在也是)
当时才学OI一个月,就被拉去考试,众人AK唯我报0,真是非常的惨
算是旧题重做吧,美其名曰“朝花夕拾”
因为DAY1 T3的期望DP被很轻松地水掉了,所以觉得这道题也是水题,结果。。。
无可奈何之下看了题解。。。
看题先:
description
小C同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。《天天爱跑步》是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。 这个游戏的地图可以看作一棵包含 nn 个结点和 n−1n−1 条边的树,每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从 11 到 nn 的连续正整数。 现在有 mm 个玩家,第 ii 个玩家的起点为 SiSi,终点为 TiTi。每天打卡任务开始时,所有玩家在第 00 秒同时从自己的起点出发,以每秒跑一条边的速度,不间断地沿着最短路径向着自己的终点跑去,跑到终点后该玩家就算完成了打卡任务。(由于地图是一棵树,所以每个人的路径是唯一的) 小C想知道游戏的活跃度,所以在每个结点上都放置了一个观察员。在结点 jj 的观察员会选择在第 WjWj 秒观察玩家,一个玩家能被这个观察员观察到当且仅当该玩家在第 WjWj 秒也正好到达了结点 jj。小C想知道每个观察员会观察到多少人? 注意:我们认为一个玩家到达自己的终点后该玩家就会结束游戏,他不能等待一段时间后再被观察员观察到。即对于把结点 jj 作为终点的玩家:若他在第 WjWj 秒前到达终点,则在结点 jj 的观察员不能观察到该玩家;若他正好在第 WjWj 秒到达终点,则在结点 jj 的观察员可以观察到这个玩家。
input
从标准输入读入数据。 第一行有两个整数 nn 和 mm。其中 nn 代表树的结点数量,同时也是观察员的数量,mm 代表玩家的数量。 接下来 n−1n−1 行每行两个整数 uu 和 vv,表示结点 uu 到结点 vv 有一条边。 接下来一行 nn 个整数,其中第 jj 个整数为 WjWj,表示结点 jj 出现观察员的时间。 接下来 mm 行,每行两个整数 SiSi 和 TiTi,表示一个玩家的起点和终点。 对于所有的数据,保证 1≤Si,Ti≤n1≤Si,Ti≤n,0≤Wj≤n0≤Wj≤n。
output
输出到标准输出。 输出 11 行 nn 个整数,第 jj 个整数表示结点 jj 的观察员可以观察到多少人。
这题题目巨长,一年前作为萌新的我压根儿没去读,直接跳。。
首先由于没人每时只走一条边,所以时间完全可以抽象成树上的距离
那么就可以把问题抽象成对于一个观察员,有多少路径经过他并且起点离他的路径为wi
为了使得答案可以dfs来解,就是使答案落在一个子树里,我们考虑把一段路径分成两段,S到lca,lca到T
那么,
x在S−>lca(S,T)上时,满足dep[x]+w[x]=dep[S]
x在lca(S,T)−>T上时,满足dep[x]−w[x]=dep[T]−len(S,T)
我们的目的就是统计这样的点数
考虑枚举x,左边的全是与x有关的,右边的全是与目标点有关的,所以我们可以搞两个桶来记录满足条件的点数
每到一个x,把向上的桶里dep[x]的位置加上起始位置在x的路径的数量,向下的桶里统计 dep[T]−len(S,T)的数量,向下dfs,在递归回来的时候统计数量
但是有时x恰好在同一条路径的S和T上,就会把这条路径统计两次。我们就在记录一个每个点作为路径的lca时的dep[S],来判断记重了多少次,减去就好
完整代码:
#include<stdio.h> #include<algorithm> #include<vector> using namespace std; const int MAXN=300000; struct edge { int v,last; }ed[MAXN<<1]; vector <int> ST[MAXN],EN[MAXN],RV[MAXN]; int n,m,num=0,anc[MAXN][18],q,head[MAXN],dep[MAXN],ans[MAXN]; int down[MAXN<<1],up[MAXN<<1],w[MAXN],show[MAXN]; void add(int u,int v) { num++; ed[num].v=v; ed[num].last=head[u]; head[u]=num; } void dfs(int u,int f) { anc[u][0]=f; for(int p=1;p<=17;p++) anc[u][p]=anc[anc[u][p-1]][p-1]; for(int i=head[u];i;i=ed[i].last) { int v=ed[i].v; if(v==f) continue ; dep[v]=dep[u]+1; dfs(v,u); } } int lca(int u,int v) { if(dep[u]<dep[v]) swap(u,v); int t=dep[u]-dep[v]; for(int p=0;p<=17;p++) if(t&(1<<p)) u=anc[u][p]; if(u==v) return u; for(int p=17;p>=0;p--) { if(anc[u][p]!=anc[v][p]) u=anc[u][p],v=anc[v][p]; } return anc[u][0]; } void GrandOrder(int u,int f) { int orgin=up[dep[u]+w[u]]+down[dep[u]-w[u]+MAXN]; up[dep[u]]+=show[u]; for(int i=0;i<EN[u].size();i++) down[EN[u][i]+MAXN]++; for(int i=head[u];i;i=ed[i].last) if(ed[i].v!=f) GrandOrder(ed[i].v,u); ans[u]=up[dep[u]+w[u]]+down[dep[u]-w[u]+MAXN]-orgin; for(int i=0;i<ST[u].size();i++) { up[ST[u][i]]--; if(ST[u][i]==dep[u]+w[u]) ans[u]--; } for(int i=0;i<RV[u].size();i++) down[RV[u][i]+MAXN]--; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v),add(v,u); } dep[1]=1; dfs(1,1); for(int i=1;i<=n;i++) scanf("%d",&w[i]); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); int x=lca(u,v),y=dep[u]+dep[v]-2*dep[x]; show[u]++; EN[v].push_back(dep[v]-y); ST[x].push_back(dep[u]); RV[x].push_back(dep[v]-y); } GrandOrder(1,1); for(int i=1;i<=n;i++) printf("%d ",ans[i]); return 0; } /* EL PSY CONGROO */
嗯,就是这样
相关文章推荐
- UOJ261 NOIP2016 day1 T2 天天爱跑步 (lca + 桶 )
- UOJ 264 NOIP2016 DAY2 T2 浅谈队列单调性及辅助队列时间戳
- UOJ 263 NOIP 2016 DAY2 T1 浅谈组合数问题
- POJ 3162 浅谈尺取法区间问题运用及多源树上路径统计
- NOIP 2016 Day1 T2 天天爱跑步
- UOJ261 【NOIP2016】天天爱跑步
- 【NOIP2016提高A组模拟8.19】树上路径
- UOJ 265 NOIP 2016 DAY2 T3 浅谈预处理状态压缩动态规划
- NOIP2016 Day2 T2 天天爱跑步(树上差分)
- JZOJ 4715 【NOIP2016提高A组模拟8.19】树上路径
- NOIP2016 day1 T2 天天爱跑步 running 题解
- 【NOIP2016提高组T2】天天爱跑步-倍增LCA+树上差分
- BZOJ 4326 NOIP 2015 DAY2 T3 浅谈二分及树上差分数组DFS动态统计
- NOIP 2016 天天爱跑步 (luogu 1600 & uoj 261) - 线段树
- 【NOIP 2016 day2 T1 T2】组合数问题,蚯蚓——题解
- 【NOIP2016提高A组模拟8.19】(雅礼联考day2)树上路径
- 【JZOJ4715】【NOIP2016提高A组模拟8.19】树上路径
- UOJ264 NOIP2016 day2 T2 蚯蚓(队列)
- JZOJ.4715【NOIP2016提高A组模拟8.19】树上路径
- NOIP2016 day1 T1 玩具谜题 toy 题解