UOJ 261 [NOIP2016 DAY1 T2] 浅谈树上路径统计问题桶解法

来源:互联网 发布:mac os 9.2弹出u盘 编辑:程序博客网 时间:2024/06/03 17:18

这里写图片描述
世界真的很大
去年考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*/

嗯,就是这样

原创粉丝点击