NOIP 2016 天天爱跑步
Description
Input
Output
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号、2号,只有0时刻出现的观察员能够看到人,所有人都在0时刻出现。那么树上差分统计人数即可。
3号、4号,也是只有0时刻观察员,只统计起点即可。
5号,暴力可过。
9-12号,由于起点固定,所以每个人跑到每个点的时间可以用性质算出来,然后按这个性质统计即可。
13-16号同理。
那么再考虑正解。
9-12和13-16号点可以给我们很大的启发:我们发现针对起点为1、终点为1的路径,都是按性质走的,所以我们就可以得到一个启发:将一条路径拆分成两个支路来解决。这样的话,这样的两条之路可以分别利用性质来处理。
这个性质是:在上行路径上,满足
[deep[s]-deep[x]=t[x]
]
这样的点是对答案有贡献的。
在下行路径上,满足
[deep[s]+deep[x]-2 imes deep[lca]=t[x]
]
这样的点是对答案有贡献的。
那么对于每一个节点(x),根据以上性质,就有(deep[s]=deep[x]+t[x]),也就是说,对于一条起点为s的上行路径来讲,它会对这个路径上所有满足这个性质的点作贡献。
下行路径同理。
那么怎么处理呢?思路到这里,权值线段树的想法就比较自然了。按起始节点s的深度deep[s]来统计人数。所以我们可以考虑用权值线段树(动态开点)来维护这个信息。然后通过树上差分快速处理新路径。最后只需要从下到上线段树合并统计即可。
注意,上行和下行要统计两遍。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char *p1,*p2,buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int read()
{
int x=0,f=1;
char ch=nc();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=nc();
}
while(ch>='0'&&ch<='9')
x=x*10+ch-'0',ch=nc();
return x*f;
}
const int maxn=3*1e5+10;
int n,m,maxx;
int tot,to[maxn<<1],nxt[maxn<<1],head[maxn];
int t[maxn];
int top[maxn],fa[maxn],deep[maxn],size[maxn],son[maxn];
int b[maxn],e[maxn],lcaa[maxn];
int sum[maxn*50],lson[maxn*50],rson[maxn*50],cnt;
int root[maxn],ans[maxn];
void add(int x,int y)
{
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs1(int x,int f)
{
deep[x]=deep[f]+1;
fa[x]=f;
size[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==f)
continue;
dfs1(y,x);
size[x]+=size[y];
if(!son[x]||size[y]>size[son[x]])
son[x]=y;
}
}
void dfs2(int x,int t)
{
top[x]=t;
if(!son[x])
return;
dfs2(son[x],t);
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==fa[x]||y==son[x])
continue;
dfs2(y,y);
}
}
int lca(int x,int y)
{
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]])
swap(x,y);
x=fa[top[x]];
}
return deep[x]<deep[y]?x:y;
}
void pushup(int pos)
{
sum[pos]=sum[lson[pos]]+sum[rson[pos]];
}
void update(int &pos,int l,int r,int x,int k)
{
int mid=(l+r)>>1;
if(!pos)
pos=++cnt;
if(l==r)
{
sum[pos]+=k;
return;
}
if(x<=mid)
update(lson[pos],l,mid,x,k);
else
update(rson[pos],mid+1,r,x,k);
pushup(pos);
}
int query(int pos,int l,int r,int x)
{
int mid=(l+r)>>1;
if(l==r)
return sum[pos];
if(x<=mid)
return query(lson[pos],l,mid,x);
else
return query(rson[pos],mid+1,r,x);
}
void merge(int &x,int y,int l,int r)
{
int mid=(l+r)>>1;
if(!x||!y)
{
x=(!x?y:x);
return;
}
if(l==r)
sum[x]+=sum[y];
merge(lson[x],lson[y],l,mid);
merge(rson[x],rson[y],mid+1,r);
}
void dfs3(int x)
{
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==fa[x])
continue;
dfs3(y);
merge(root[x],root[y],1,maxx);
}
if(n+deep[x]+t[x]<=maxx)
ans[x]+=query(root[x],1,maxx,n+deep[x]+t[x]);
}
void dfs4(int x)
{
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==fa[x])
continue;
dfs4(y);
merge(root[x],root[y],1,maxx);
}
ans[x]+=query(root[x],1,maxx,n+deep[x]-t[x]);
}
int main()
{
n=read();m=read();
for(int i=1;i<n;i++)
{
int x,y;
x=read();y=read();
add(x,y);
add(y,x);
}
for(int i=1;i<=n;i++)
t[i]=read();
dfs1(1,0);
dfs2(1,1);
maxx=n<<1;
for(int i=1;i<=m;i++)
{
b[i]=read(),e[i]=read();
lcaa[i]=lca(b[i],e[i]);
update(root[b[i]],1,maxx,deep[b[i]]+n,1);
update(root[fa[lcaa[i]]],1,maxx,deep[b[i]]+n,-1);
}
dfs3(1);
cnt=0;
memset(root,0,sizeof(root));
memset(sum,0,sizeof(sum));
memset(lson,0,sizeof(lson));
memset(rson,0,sizeof(rson));
for(int i=1;i<=m;i++)
{
update(root[e[i]],1,maxx,n-deep[b[i]]+2*deep[lcaa[i]],1);
update(root[lcaa[i]],1,maxx,n-deep[b[i]]+2*deep[lcaa[i]],-1);
}
dfs4(1);
for(int i=1;i<n;i++)
printf("%d ",ans[i]);
printf("%d",ans[n]);
return 0;
}