[NOIP]2016天天爱跑步

[NOIP]2016天天爱跑步

标签: LCA 树上差分 NOIP


Description

小C同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。《天天爱跑步》是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。

这个游戏的地图可以看作一棵包含n个结点和n-1条边的树,每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从1到n的连续正整数。

现在有m个玩家,第i个玩家的起点为Si,终点为Ti。每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发,以每秒跑一条边的速度,不间断地沿着最短路径向着自己的终点跑去,跑到终点后该玩家就算完成了打卡任务。(由于地图是一棵树,所以每个人的路径是唯一的)

Input

第一行有两个整数n和m。其中n代表树的结点数量,同时也是观察员的数量,m代表玩家的数量。

接下来n-1行每行两个整数u和v,表示结点u到结点v有一条边。

接下来一行n个整数,其中第j个整数为Wj,表示结点j出现观察员的时间。

接下来m行,每行两个整数Si和Ti,表示一个玩家的起点和终点。

对于所有的数据,保证1≤Si,Ti≤n,0≤ Wj ≤n。

Output

输出1行n个整数,第j个整数表示结点j的观察员可以观察到多少人。

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号点,玩家2被观察到,共1人被观察到。

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

Pic

如果你的程序需要用到较大的栈空间(这通常意味着需要较深层数的递归),请务必仔细阅读选手目录下的文档running/stackpdf,以了解在最终评测时栈空间的限制与在当前工作环境下调整栈空间限制的方法。


题解

  • 对于一个玩家,他的路径我们可以拆成两段。分别是u->lca,lca->v。对于每一个观察者x,如果deep[x]+w[x]deep[u]时,u->lca的这段路径可能会更新答案。同理,deep[x]-w[x]deep[v]-dis(u,v)时,lca->v的这段路径也有可能更新答案。
  • 我们只考虑u->lca,只有当x在u->lca上时,答案才会被更新。我们就很难统计。不过我们可以利用差分的思想,把这条路径拆成u->root(贡献为1),fa[lca]->root(贡献为-1),就容易统计一些了。
  • 然后我们对树进行dfs,在dfs的过程中,对每一个子树的根u来说,遍历子树,再加上自己这个点的值,然后计算这个点被访问的次数,相当于求一遍前缀和,只是加上了自己所有子树的值。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define REP(i,a,b) for(int i=(a),_end_=(b);i<=_end_;i++)
#define DREP(i,a,b) for(int i=(a),_end_=(b);i>=_end_;i--)
#define EREP(i,a) for(int i=start[(a)];i;i=e[i].next)
inline int read()
{
int sum=0,p=1;char ch=getchar();
while(!(('0'<=ch && ch<='9') || ch=='-'))ch=getchar();
if(ch=='-')p=-1,ch=getchar();
while('0'<=ch && ch<='9')sum=sum*10+ch-48,ch=getchar();
return sum*p;
} const int maxn=3e5+20; struct node {
int v,next;
};
node e[maxn*2];
int cnt,start[maxn];
int n,m; void addedge(int u,int v)
{
e[++cnt]={v,start[u]};
start[u]=cnt;
} int deep[maxn],p[maxn][21]; void dfs(int u,int fa)
{
p[u][0]=fa;
EREP(i,u)
{
int v=e[i].v;
if(v==fa)continue;
deep[v]=deep[u]+1;
dfs(v,u);
}
} int lca(int u,int v)
{
if(deep[u]<deep[v])swap(u,v);
DREP(i,20,0)if(p[u][i]!=-1 &&deep[p[u][i]]>=deep[v])u=p[u][i];
if(u==v)return u;
DREP(i,20,0)if(p[u][i]!=-1 && p[u][i]!=p[v][i])u=p[u][i],v=p[v][i];
return p[u][0];
} struct player {
int t,w,next,id;
};
player ed[maxn*4];
int st[maxn],cn; void add(int u,int t,int w,int idx)
{
ed[++cn]={t,w,st[u],idx};
st[u]=cn;
} int A[maxn],B[maxn];
int ans[maxn];
int CntA[maxn*2],CntB[maxn*2]; void dfs1(int u,int fa)
{
int la=CntA[A[u]];
int lb=CntB[B[u]+maxn];
for(int i=st[u];i;i=ed[i].next)
{
int x=ed[i].t;
if(ed[i].id==1)
{
CntA[x]+=ed[i].w;
}else
{
CntB[x+maxn]+=ed[i].w;
}
}
EREP(i,u)
{
int v=e[i].v;
if(v==fa)continue;
dfs1(v,u);
}
int na=CntA[A[u]];
int nb=CntB[B[u]+maxn];
ans[u]+=(na-la)+(nb-lb);
} void init()
{
n=read();m=read();
REP(i,1,n-1)
{
int u=read(),v=read();
addedge(u,v);
addedge(v,u);
}
deep[1]=1;
dfs(1,0);
REP(i,1,n)
{
int w=read();
A[i]=deep[i]+w;
B[i]=deep[i]-w;
}
REP(j,1,20)REP(i,1,n)p[i][j]=p[p[i][j-1]][j-1];
REP(i,1,m)
{
int u=read(),v=read(),Lca=lca(u,v);
int len=deep[u]+deep[v]-2*deep[Lca];//点用链表储存,方便调用
add(u,deep[u],1,1);
add(p[Lca][0],deep[u],-1,1);
add(v,deep[v]-len,1,2);
add(Lca,deep[v]-len,-1,2);
}
dfs1(1,0);
REP(i,1,n)
cout<<ans[i]<<" ";
}
int main()
{
freopen("input.in","r",stdin);
freopen("output.out","w",stdout);
init();
return 0;
}
上一篇:解决Socket.IO在IE8下触发disconnect时间过长


下一篇:数据库的未来:ORM+LINQ+RX