NOIP2016 天天爱跑步 (树上差分+dfs)

3 篇文章 0 订阅
1 篇文章 0 订阅

题目大意:给你一颗树,树上每个点都有一个观察员,他们仅会在 w[i] 时刻出现,观察正在跑步的玩家

一共有m个玩家,他们分别从节点 s[i] 同时出发,以每秒跑一条边的速度,沿着到 t[i] 的唯一路径向节点t[i]奔跑

如果一名玩家已经到达了终点,那么在他到达终点之后出现在终点的观察员不会观察到他

但如果在到达终点的同时观察员也出现在终点,那么观察员可以观察到他

求每个节点的观察员观察到玩家的数量

 

对于每个玩家的奔跑路线,可以拆成两部分

<1>向上跑,从 u 向 lca 奔跑

显然,玩家 u 能对观察员 i 产生1点贡献的充要条件为

deep[u]=deep[i]+w[i]

显然,向上走的路线能对 i 点产生共贡献的条件是,起点 u 在 i 的子树中

我们只需要统计符合条件的deep[u]即可

<2>向下跑,从 lca 向 v 奔跑

那么显然dis<u,v>-dis<i,v>=w[i]

推导可得deep[u]-2*deep[lca]=w[i]-deep[i]

显然,向下走的路线能对i点产生共贡献的条件是,终点 v 在 i 的子树中

类似于上面的方法,我们只需要统计符合条件的deep[u]-2*deep[lca]即可

我们可以用vector在u,v,lca这三个点上打差分,存符合条件的值,注意值可能为负数

而其他子树的结果会影响答案,所以深搜统计一次答案,再用回溯的答案减去深搜的答案即可

方法比较奇葩大家凑合看吧。。。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define N 300010
#define maxn 300000
using namespace std;

int n,m,cte;
int dep[N],fa[N],tp[N],sz[N],son[N],ans[N];
int w[N],s[N],t[N],F[N],bac[N*3],head[N],tmp[N];
struct EDGE{
    int to,nxt;
}edge[N*2];
vector<int>S[N];
vector<int>E[N];
int gc()
{
    int rett=0,fh=1;char p=getchar();
    while(p<'0'||p>'9') {if(p=='-')fh=-1;p=getchar();}
    while(p>='0'&&p<='9') {rett=rett*10+p-'0';p=getchar();}
    return rett*fh;
}
void edge_add(int u,int v)
{
    cte++;
    edge[cte].to = v;
    edge[cte].nxt=head[u];
    head[u]=cte;
}
void Tsp1(int u,int dad)
{
    for(int j=head[u];j!=-1;j=edge[j].nxt)
    {
        int v=edge[j].to;
        if(v==dad) continue;
        dep[v]=dep[u]+1;
        fa[v]=u;
        Tsp1(v,u);
        if(sz[v]>sz[son[u]]) son[u]=v;
        sz[u]+=sz[v];
    }
    sz[u]++;
}
void Tsp2(int u)
{
    if(son[u]) tp[son[u]]=tp[u],Tsp2(son[u]);
    for(int j=head[u];j!=-1;j=edge[j].nxt)
    {
        int v=edge[j].to;
        if(v==fa[u]||v==son[u]) continue;
        tp[v]=v;
        Tsp2(v);
    }
}
int LCA(int x,int y)
{
    while(tp[x]!=tp[y])
    {
        if(dep[tp[x]]<dep[tp[y]]) swap(x,y);
        x=fa[tp[x]];
    }
    return dep[x]<=dep[y]?x:y;
}
void dfs1(int u)
{
    tmp[u]=bac[dep[u]+w[u]+maxn];
    for(int j=head[u];j!=-1;j=edge[j].nxt)
    {
        int v=edge[j].to;
        if(v==fa[u]) continue;
        dfs1(v);
    }
    for(int i=0;i<S[u].size();i++)
        bac[S[u][i]+maxn]++;
    ans[u]+=bac[dep[u]+w[u]+maxn]-tmp[u];
    for(int i=0;i<E[u].size();i++)
        bac[E[u][i]+maxn]--;
}
void dfs2(int u)
{
    tmp[u]=bac[w[u]-dep[u]+maxn];
    for(int j=head[u];j!=-1;j=edge[j].nxt)
    {
        int v=edge[j].to;
        if(v==fa[u]) continue;
        dfs2(v);
    }
    for(int i=0;i<E[u].size();i++)
        bac[E[u][i]+maxn]--;
    for(int i=0;i<S[u].size();i++)
        bac[S[u][i]+maxn]++;
    ans[u]+=bac[w[u]-dep[u]+maxn]-tmp[u];
}

int main()
{
    freopen("running1.in","r",stdin);
    //freopen("running6.out","w",stdout);
    scanf("%d%d",&n,&m);
    int x,y;
    memset(head,-1,sizeof(head));
    for(int i=1;i<n;i++)
    {
        x=gc(),y=gc();
        edge_add(x,y);
        edge_add(y,x);
    }
    tp[1]=1;
    Tsp1(1,-1);
    Tsp2(1);
    for(int i=1;i<=n;i++) w[i]=gc();
    for(int i=1;i<=m;i++)
    {
        s[i]=gc(),t[i]=gc();
        F[i]=LCA(s[i],t[i]);
        S[s[i]].push_back(dep[s[i]]);
        E[F[i]].push_back(dep[s[i]]);
    }
    //debug();
    dfs1(1);
    for(int i=1;i<=n;i++) 
    {
        S[i].clear(),E[i].clear();
    }
    memset(tmp,0,sizeof(tmp));
    for(int i=1;i<=m;i++)
    {
        S[t[i]].push_back(dep[s[i]]-2*dep[F[i]]);
        E[F[i]].push_back(dep[s[i]]-2*dep[F[i]]);
    }
    dfs2(1);
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值