终于把去年noip的坑填完了啊。。。好可怕。。。
这道题的做法是LCA+差分+桶。(其实只能算是半个差分)
首先我们要确定搜索对象。玩家的数量那么多而且还在移动显然不能成为搜索对象。所以我们要搜索的是观察员。
然后我们观察题目,我们发现若当前搜索到点x,如果某个玩家的路径经过x,那么这个玩家的路径起点或终点必然在x的子树中。
然后我们要寻找性质来使得时间从动态转化为静态。
我们发现可以将玩家的路径拆分为start到lca,再从lca到end。
若某个玩家的路径起点在x的子树中,我们可以发现,若这个玩家所在的位置为i,出发time[i]的时间,那么
time[i]=deep[start]−deep[i]
移项得到
deep[i]+time[i]=deep[start]
我们可以发现等式左边和右边都是定值。
同样,若某个玩家的路径终点在x的子树中,路径长度为len,那么
deep[end]−deep[i]=len−time[i]
将len拆解为 deep[start]+deep[end]−2∗deep[lca] ,代入原式,化简得到
deep[i]−time[i]=2∗deep[lca]−deep[start]
等式左边和右边同样都是定值。
这样我们就可以使用两个桶,分别维护从start到lca,再从lca到end两段,上述两个等式右边可以用作玩家在桶中使用的下标,等式左边可以用作观察员在桶中使用的下标。注意:第二个等式在使用的时候可能会出现负数,放到桶里之前应该先加上一个常数。
CODE:
#include<cstdio>
#include<cstdlib>
const int N=3e5+10;
struct edge
{
int nxt,to;
}a[N<<1];
struct Edge
{
int nxt,to;
bool tmp,dec;
}e[N<<1];
struct node
{
int nxt,to;
bool tmp;
}E[N<<1];
int head[N],Head[N],h[N],deep[N],f[N],size[N],son[N],top[N];
int w[N],t1[N<<1],t2[N<<1],ans[N];
int n,m,x,y,num,Num=1,tot,cnt;
inline void add(int x,int y)
{
a[++num].nxt=head[x],a[num].to=y,head[x]=num;
a[++num].nxt=head[y],a[num].to=x,head[y]=num;
}
inline void Add(int x,int y)
{
e[++Num].nxt=Head[x],e[Num].to=y,e[Num].tmp=1,Head[x]=Num;
e[++Num].nxt=Head[y],e[Num].to=x,e[Num].tmp=0,Head[y]=Num;
}
inline void insert(int x,int y,bool z)
{
E[++cnt].nxt=h[x],E[cnt].to=y,E[cnt].tmp=z,h[x]=cnt;
}
void dfs(int now,int fa,int depth)
{
f[now]=fa,deep[now]=depth;
size[now]=1;
int tmp=0;
for(int i=head[now];i;i=a[i].nxt)
if(a[i].to!=fa)
{
dfs(a[i].to,now,depth+1);
size[now]+=size[a[i].to];
if(size[a[i].to]>tmp) tmp=size[a[i].to],son[now]=a[i].to;
}
}
void dfs2(int now,int high)
{
top[now]=high;
if(son[now]) dfs2(son[now],high);
for(int i=head[now];i;i=a[i].nxt)
if(a[i].to!=f[now]&&a[i].to!=son[now]) dfs2(a[i].to,a[i].to);
}
inline int LCA(int x,int y)
{
while(top[x]!=top[y])
if(deep[top[x]]>deep[top[y]]) x=f[top[x]];
else y=f[top[y]];
return deep[x]<deep[y]?x:y;
}
void solve(int now)
{
int tmp1=t1[deep[now]+w[now]+N],tmp2=t2[deep[now]-w[now]+N];
for(int i=Head[now];i;i=e[i].nxt)
{
int lca=LCA(now,e[i].to);
if(!e[i].dec)
{
if(deep[lca]+w[lca]==deep[e[i].tmp?now:e[i].to]) ans[lca]--;
e[i].dec=e[i^1].dec=1;
}
if(e[i].tmp) t1[deep[now]+N]++,insert(lca,now,0);
else t2[(deep[lca]<<1)-deep[e[i].to]+N]++,insert(lca,e[i].to,1);
}
for(int i=head[now];i;i=a[i].nxt)
if(a[i].to!=f[now]) solve(a[i].to);
ans[now]+=t1[deep[now]+w[now]+N]-tmp1+t2[deep[now]-w[now]+N]-tmp2;
for(int i=h[now];i;i=E[i].nxt)
if(E[i].tmp) t2[(deep[now]<<1)-deep[E[i].to]+N]--;
else t1[deep[E[i].to]+N]--;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
scanf("%d%d",&x,&y),add(x,y);
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
if(x==y)
if(!w[x]) ans[x]++;
else;
else Add(x,y);
}
dfs(1,0,1),dfs2(1,1);
solve(1);
for(int i=1;i<n;i++)
printf("%d ",ans[i]);
printf("%d",ans[n]);//bzoj卡最后一行的空格
return 0;
}