天天爱跑步(NOIP2016)

传送门

终于把这个坑填了!

思路还是很值得学习的,这篇博客写的很详细。

重要的是拆成两种路径,推出这两个公式:

up:T[i]+dep[i]=dep[s];

down:dep[v]-dis(u,v)=dep[i]-T[i]

两次dfs分别对应up和down两种情况,dfs里对于每一个i,统计其子树内满足等式的点s的个数(用捅保存),这样就对ans[i]做出贡献。

每次x处理完后要删去桶中以i为LCA的路径的起点深度桶的值。

因为当我们遍历完i节点的孩子时,对于以i节点为LCA的路径来说。

这条路径上的信息对i的祖先节点是不会有影响的。(因为lca就是其能走到的最高的点了)

所以要将其删去。

注意:(摘自洛谷题解)

          还有就是down里面下标可能为负,全部加一个N即可。

          

#include<bits/stdc++.h>
#define N 300002
using namespace std;
int read()
{
    int x=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
struct EDGE{
    int nextt,to;
}w[N*2];
struct play{
    int s,t,lca,len;
}p[N];
int tot=0; 
int head[N],f[N][21],dep[N],T[N],tong[N],tong2[N],num[N],ans[N];
vector<int>a[N],b[N],c[N];
void add(int a,int b)
{
    tot++;
    w[tot].nextt=head[a];
    w[tot].to=b;
    head[a]=tot;
}
void dfs(int x)
{
    for(int i=head[x];i;i=w[i].nextt)
    {
        int v=w[i].to;
        if(v==f[x][0])continue;
        dep[v]=dep[x]+1;
        f[v][0]=x;
        for(int i=1;i<=20;++i)f[v][i]=f[f[v][i-1]][i-1];
        dfs(v);
    }
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int i=20;i>=0;--i)if(dep[f[x][i]]>=dep[y])x=f[x][i];
    if(x==y)return x;
    for(int i=20;i>=0;--i)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    return f[x][0];
}
void dfs_up(int x)//T[i]+dep[i]=dep[s];
{//x作为lca的贡献//只要符合dep[i]+T[i]的是玩家起点的点,就能对i点产生贡献
    int now=dep[x]+T[x],last=tong[now];//now即s,深度大于x,已经更新过了 
    for(int i=head[x];i;i=w[i].nextt)
    {
        int v=w[i].to;
        if(v==f[x][0])continue;
        dfs_up(v);
    }
    tong[dep[x]]+=num[x];//为可以成为它的lca的点做准备 
    ans[x]+=tong[now]-last;//跨子树的情况所以减去last ans[i]加上的其实就是i的子树对i的贡献
    for(int i=0;i<a[x].size();++i)tong[dep[a[x][i]]]--;//x退出,作为lca的贡献减去 
}
void dfs_down(int x)//dep[v]-dis(u,v)=dep[i]-T[i] 
{
    int now=dep[x]-T[x],last=tong2[now+N];//now可能为负,处理时都加上一个N 
    for(int i=head[x];i;i=w[i].nextt)
    {
        int v=w[i].to;
        if(v==f[x][0])continue;
        dfs_down(v);
    }
    for(int i=0;i<b[x].size();++i)tong2[b[x][i]+N]++;
    ans[x]+=tong2[now+N]-last;
    for(int i=0;i<c[x].size();++i)tong2[c[x][i]+N]--;
} 
int main()
{
    int n=read(),m=read();
    for(int i=1;i<n;++i)
    {
        int a=read(),b=read();
        add(a,b);add(b,a);
    }
    dep[1]=1;
    dfs(1);
    for(int i=1;i<=n;++i)
      T[i]=read();
    for(int i=1;i<=m;++i)
    {
        p[i].s=read();p[i].t=read();
        num[p[i].s]++;//这里可以直接用num保存s的个数,而down不行是因为每一个的dis是不一样的 
        p[i].lca=LCA(p[i].s,p[i].t);
        p[i].len=dep[p[i].s]+dep[p[i].t]-2*dep[p[i].lca];
        a[p[i].lca].push_back(p[i].s);
        b[p[i].t].push_back(dep[p[i].t]-p[i].len);//num和b的实质其实是一样的 
        c[p[i].lca].push_back(dep[p[i].t]-p[i].len);
    }
    dfs_up(1);
    dfs_down(1);
    for(int i=1;i<=m;++i)
      if(dep[p[i].s]-dep[p[i].lca]==T[p[i].lca])ans[p[i].lca]--;//重复算了,位于lca的位置 
    for(int i=1;i<=n;++i)printf("%d ",ans[i]);
}
View Code
原文地址:https://www.cnblogs.com/yyys-/p/11366268.html