洛谷 P1600 天天爱跑步

95 篇文章 1 订阅
39 篇文章 0 订阅

题面就不粘贴了

/*把每个玩家的路径拆成一条到LCA的路径和从LCA到终点的路径~ 然后,使用树上差分统计答案即可~ 
那么,树上差分是什么? 差分的具体思想是,当某区间内某元素对答案有贡献,就在区间起点打一个+1
标记代表多出了一个对答案有贡献的元素,在终点打一个-1标记代表一个对答案有贡献的元素在该位置
结束了它的使命。 于是,统计答案就变成了维护扫到当前位置为止的标记个数,直接累加就是答案~ 首先
对从下到上的路径观察~ 然后就会发现:所有对某点上的观察员i有贡献的点只可能是以i点往下w[i]层深
度的任一后代为起点的玩家~ 然后我们就可以维护一个标记数组,每个位置分别表示当前共访问了多少个深
度为当前位置下标的点。 直接dfs,先对目标深度,也就是对当前观察员i有贡献的深度为dep[i]+w[i]的
标记,记录其当前的值。 然后递归搜索所有子树,每搜到一个点便把以当前点为起点的所有路径加入当前点深
度的标记中,代表这些点开始产生贡献,并把以当前点为LCA的所有路径从从该路径起点所在深度中减去,代
表这些点停止产生贡献。 当回溯到当前点,统计搜索完所有子树后dep[i]+w[i]处标记的新值,并减去记
录下的旧的值,作为当前节点的答案~ 这样搜一遍,正向的便被统计完了~ 从LCA到终点的方法同理,只是
在dep[i]-w[i]时可能有负数出现,加上30000的偏移量即可~ */


#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN = 302333;
#define pb push_back
struct Player{ int s,t,lca,len; }a[MAXN];
int to[MAXN<<1],nxt[MAXN<<1],head[MAXN<<1],n,m,w[MAXN],deep,tot;
vector<int> upper[MAXN],down1[MAXN],down2[MAXN];
inline void read(int &x){
    x=0; int f=1; char c=getchar();
    while(c>'9'||c<'0'){ if(c=='-') f=-1; c=getchar(); }
    while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); } x*=f;
}

inline void Add_(int u,int v){ to[++tot]=v,nxt[tot]=head[u],head[u]=tot; }
inline void Add_Edge(int u,int v){ Add_(u,v),Add_(v,u); }
int top[MAXN],dep[MAXN],siz[MAXN],son[MAXN],fa[MAXN];
int pos[MAXN],ans[MAXN],delta[MAXN<<1];

void DFS1(int u,int father,int deepth){
    fa[u]=father,dep[u]=deepth,siz[u]=1,son[u]=0;
    deep=max(deep,deepth);
    for(int i=head[u];i;i=nxt[i]){
        int v=to[i];
        if(v==father) continue;
        DFS1(v,u,deepth+1);
        siz[u]+=siz[v];
        if(!son[u]||siz[v]>siz[son[u]]) son[u]=v;
    }
}

void DFS2(int u,int Top){
    top[u]=Top;
    if(son[u]) DFS2(son[u],Top);
    for(int i=head[u];i;i=nxt[i]){
        int v=to[i];
        if(v!=fa[u]&&v!=son[u]) DFS2(v,v);
    }
}

inline int Get_Lca(int u,int v){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    return u;
}

void DFS3(int u){
    int now=dep[u]+w[u],last=delta[now];
    for(int i=head[u];i;i=nxt[i])
        if(to[i]!=fa[u]) DFS3(to[i]);
    delta[dep[u]]+=pos[u];
    if(now<=deep) ans[u]=delta[now]-last;
    for(int i=0;i<upper[u].size();++i)
        delta[dep[upper[u][i]]]--;
}

#define W30 300000

void DFS4(int u){
    int now=dep[u]-w[u]+W30,last=delta[now];
    for(int i=head[u];i;i=nxt[i])
        if(to[i]!=fa[u]) DFS4(to[i]);
    for(int i=0;i<down1[u].size();++i)
        ++delta[down1[u][i]+W30];
    ans[u]+=delta[now]-last;
    for(int i=0;i<down2[u].size();++i)
        --delta[down2[u][i]+W30];
}

int main(){
    read(n),read(m);
    for(int i=1,u,v;i<n;++i)
        read(u),read(v),Add_Edge(u,v);
    DFS1(1,1,1);
    top[1]=1;
    DFS2(1,1);
    for(int i=1;i<=n;++i) read(w[i]);
    for(int i=1;i<=m;++i){
        read(a[i].s),read(a[i].t);
        a[i].lca=Get_Lca(a[i].s,a[i].t);
        a[i].len=dep[a[i].s]+dep[a[i].t]-(dep[a[i].lca]<<1);
        pos[a[i].s]++;
        upper[a[i].lca].pb(a[i].s);
    }
    DFS3(1);
    for(int i=1;i<=m;++i){
        down1[a[i].t].pb(dep[a[i].t]-a[i].len);
        down2[a[i].lca].pb(dep[a[i].t]-a[i].len);
    }
    memset(delta,0,sizeof delta );
    DFS4(1);
    for(int i=1;i<=m;++i)
        if(dep[a[i].s]==dep[a[i].lca]+w[a[i].lca])
            --ans[a[i].lca];
    printf("%d",ans[1]);
    for(int i=2;i<=n;++i) printf(" %d",ans[i]);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七情六欲·

学生党不容易~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值