【NOIP2016提高组 day1】天天爱跑步

【NOIP2016提高组 day1】天天爱跑步_第1张图片
【NOIP2016提高组 day1】天天爱跑步_第2张图片
【NOIP2016提高组 day1】天天爱跑步_第3张图片

分析

这道题思维程度实在高 (反正蒟蒻在考场上打不出来orz)
本不该是第二题的难度QWQ

运用LCA+桶+树上差分
对于当前点x,找出对它所有有贡献的点(是起点,并且能够在w[i]时间到达i点)
先用树上倍增/Tarjan求出所有起点s[i]和终点t[i]的LCA,并用dis[i]数组保存s[i]到t[i]的距离。

怎么找对每一个点有贡献的点呢?
设起点u,终点v,u和v的最近公共祖先lca,deep[x]表示点x的深度。此时的dis[i]为u到v的距离

情况一: 从u经过点i到 lca (即从下向上走)
若deep[x]+w[x]=deep[u]
则点u对于i有贡献 因为(从u点出发到达i点,刚好经过w[i]时间)

情况二: 从u经过lca到点i (即先向上再向下走)
若w[x]-deep[x]=dis[i]-deep[v]
则点v对于i有贡献(等同于点u对于i有贡献
(同理从u点出发到达i点,刚好经过w[i]时间)

这个可能说起来有点抽象,但只要画个图就一目了然啦~

最后将两种情况合并起来,统计(运用dfs从下向上统计)所有对当前点x有贡献的点的个数,保存在ans[x]中,最后输出就可以啦。

代码如下

#include<bits/stdc++.h>
using namespace std;
const int N=300000;
int n,m,a[N],head[N],h1[N],h2[N],f[N][20];
int deep[N],s[N],t[N],st[N]; 
int w[N],ans[N],tot,c1,c2;
int b1[N*2],b2[N*2],dis[N];
struct edge{
    int ver,to;
}e[N*2],e1[N*2],e2[N*2];
int read(){
    int sum=0,f=1;
    char ch=getchar();
    while(ch>'9'||ch<'0')
    {
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        sum=(sum<<3)+(sum<<1)+ch-'0';
        ch=getchar();
    }
    return sum*f;
}
void add(int x,int y)
{
    e[++tot].ver=y;
    e[tot].to =head[x];
    head[x]=tot;
}
void add1(int x,int y)
{
    e1[++c1].ver=y;
    e1[c1].to =h1[x];
    h1[x]=c1;
}
void add2(int x,int y)
{
    e2[++c2].ver=y;
    e2[c2].to =h2[x];
    h2[x]=c2;
}
void dfs(int x){
    for(int i=1;(1<<i)<=deep[x];i++){
       f[x][i]=f[f[x][i-1]][i-1];		
    }
    for(int i=head[x];i;i=e[i].to){
        int y=e[i].ver;
        if(y==f[x][0])continue;
        f[y][0]=x;
        deep[y]=deep[x]+1;
        dfs(y);
    }
}
int lca(int x,int y){
    if(x==y)return x;
    if(deep[y]>deep[x])swap(x,y);
    int t=log(deep[x]-deep[y])/log(2);
    for(int i=t;i>=0;i--)
    if(deep[f[x][i]]>=deep[y])
    {
      x=f[x][i];
    }
    if(x==y)return x;
    t=log(deep[x])/log(2);
    for(int i=t;i>=0;i--)
        {
            if(f[x][i]!=f[y][i])
            {
                x=f[x][i];
                y=f[y][i];
            }
        }
    return f[x][0];
}
void dfs2(int x){
    int t1=b1[w[x]+deep[x]],t2=b2[w[x]-deep[x]+N];
    for(int i=head[x];i;i=e[i].to){
        int y=e[i].ver;
        if(y==f[x][0])continue;
        dfs2(y);
    }
    b1[deep[x]]+=st[x];
    for(int i=h1[x];i;i=e1[i].to){
        int y=e1[i].ver;
        b2[dis[y]-deep[t[y]]+N]++;
    }
    ans[x]+=b1[w[x]+deep[x]]-t1+b2[w[x]-deep[x]+N]-t2;
    for(int i=h2[x];i;i=e2[i].to){
        int y=e2[i].ver;
        b1[deep[s[y]]]--;
        b2[dis[y]-deep[t[y]]+N]--;
    }
} 
int main(){
//	freopen("running.in","r",stdin);
//	freopen("running.out","w",stdout);
    n=read();
    m=read();
    int u,v;
    for(int i=1;i<n;i++)
    {
        u=read();
        v=read();
        add(u,v);
        add(v,u);
    } 
    deep[1]=1;
    f[1][0]=1;
    dfs(1);
    for(int i=1;i<=n;i++)
     w[i]=read();
    for(int i=1;i<=m;i++)
    {
        s[i]=read();
        t[i]=read();
        int fx=lca(s[i],t[i]);
        st[s[i]]++;
        dis[i]=deep[s[i]]+deep[t[i]]-2*deep[fx];
        add1(t[i],i);
        add2(fx,i);
        if(deep[fx]+w[fx]==deep[s[i]]) ans[fx]--;
    }
    dfs2(1);
    for(int i=1;i<=n;i++)
    cout<<ans[i]<<' ';
    return 0;
} 

你可能感兴趣的:(NOIP2016提高组)