• 天天爱跑步(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
  • 相关阅读:
    golang_并发安全: slice和map并发不安全及解决方法
    什么情况下需要用到互斥锁sync.Mutex?
    使用Charles进行HTTPS抓包
    centos6 yum 源失效 404,终于解决了
    GOMAXPROCS你设置对了吗?
    容器资源可见性问题与 GOMAXPROCS 配置
    gflags 使用方式
    分布式训练问题
    NCCL常用环境变量
    git 常用命令
  • 原文地址:https://www.cnblogs.com/yyys-/p/11366268.html
Copyright © 2020-2023  润新知