終於把去年noip的坑填完了啊。。。好可怕。。。
這道題的做法是LCA+差分+桶。(其實只能算是半個差分)
首先我們要確定搜索對象。玩家的數量那麼多而且還在移動顯然不能成爲搜索對象。所以我們要搜索的是觀察員。
然後我們觀察題目,我們發現若當前搜索到點x,如果某個玩家的路徑經過x,那麼這個玩家的路徑起點或終點必然在x的子樹中。
然後我們要尋找性質來使得時間從動態轉化爲靜態。
我們發現可以將玩家的路徑拆分爲start到lca,再從lca到end。
若某個玩家的路徑起點在x的子樹中,我們可以發現,若這個玩家所在的位置爲i,出發time[i]的時間,那麼
移項得到
我們可以發現等式左邊和右邊都是定值。
同樣,若某個玩家的路徑終點在x的子樹中,路徑長度爲len,那麼
將len拆解爲
等式左邊和右邊同樣都是定值。
這樣我們就可以使用兩個桶,分別維護從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;
}