您的位置:首页 > 其它

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
*/


嗯,就是这样
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: