zoukankan      html  css  js  c++  java
  • P1600 天天爱跑步 (lca+树上差分+树上开桶)

    P1600 天天爱跑步

    分析:

    首先若想对于每一个人统计他的路线对观察点的贡献,显然是很难优化到log的。

    考虑对每一个观察点统计答案。

    一个观察点会被这样两种路径经过:

    1.起点在其下方,从起点向上走经过它。

    2.终点在其下方,从某一处的起点向下走经过它。

    对于第一种情况,我们要满足:dep[ s ] == dep[ x ] + w[ x ](x是观察点,就是说s在x点的下面,过了w[ x ]秒正好到达x点)

    对于第二种情况,我们要满足:tim=dep[ s ] + dep[ t ] - 2*dep[ lc ](从s到t经过的时间) tim - (dep[ t ] - dep[ x ]) == w[ x ](总体花费的时间-从x走下来到 t 花费的时间,就是从s走到x花费的时间)

    第二种化简一下:dep[ s ] - 2 * dep [ lc ] == w[ x ] - dep[ x ]

    两种情况后半部分都只与观察点有关,当一个观察点x的子树中,有满足以上两个等式就将ans[ x ]++。

    所以我们只需要对上面左边的式子开一个桶记录一下即可。

    但是会出现起点终点都在x一下,从头到尾都不经过x的,怎么办呢?

    树上差分!

    对于第一种,在fa[ lc ]上的桶减去贡献即可。

    对于第二种,在lc上的桶减去贡献。(为什么不是fa[ lc ]:lc已经归结在第一种情况里面了,在第二种的半路径里是不包括lc的!!)

    但还有一个问题:统计子树的时候他们是共用一个桶,信息重复了怎么办?

    设u的两个子节点v1和v2。

    当v1遍历完后,桶中保留了v1及其所有子节点的信息。

    这时我们遍历v2,将v2子树中的信息也加入桶里,那么统计v2的贡献时明显会统计到v1子树中的,怎么办呢?

    只需要在将v2子树中信息加入前记录一下原来的,然后加入后再两者相减即可。

    #include<bits/stdc++.h>
    using namespace std;
    #define N 300005
    #define ri register int
    vector<int> a1[N],a2[N],b1[N*3],b2[N*3],e[N];
    int sum1[N<<1],sum2[N<<1],fa[N][22],w[N],n,m,ans[N],dep[N];
    void dfs1(int u,int ff)
    {
        for(ri i=0;i<e[u].size();++i){
            int v=e[u][i];
            if(v==ff) continue;
            fa[v][0]=u; dep[v]=dep[u]+1;
            for(ri j=1;j<=20;++j) fa[v][j]=fa[fa[v][j-1]][j-1];
            dfs1(v,u);
        }
    }
    int lca(int a,int b)
    {
        if(dep[a]<dep[b]) swap(a,b);
        for(ri i=20;i>=0;--i) if(dep[fa[a][i]]>=dep[b]) a=fa[a][i];
        if(a==b) return a;
        for(ri i=20;i>=0;--i) if(fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i];
        return fa[a][0];
    }
    void dfs2(int u,int ff)
    {
        int tmp1=sum1[dep[u]+w[u]],tmp2=sum2[w[u]-dep[u] +n];
        for(ri i=0;i<e[u].size();++i) if(e[u][i]!=ff) dfs2(e[u][i],u);
        for(ri i=0;i<a1[u].size();++i) sum1[a1[u][i]]++;
        for(ri i=0;i<a2[u].size();++i) sum1[a2[u][i]]--;
        for(ri i=0;i<b1[u].size();++i) sum2[b1[u][i]]++;
        for(ri i=0;i<b2[u].size();++i) sum2[b2[u][i]]--;
        ans[u]=sum1[dep[u]+w[u]]-tmp1 + sum2[w[u]-dep[u] +n]-tmp2;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        int a,b;
        for(ri i=1;i<=n-1;++i) scanf("%d%d",&a,&b),e[a].push_back(b),e[b].push_back(a);
        dep[0]=-1; dep[1]=0; dfs1(1,0);
        for(ri i=1;i<=n;++i) scanf("%d",&w[i]);
        for(ri i=1;i<=m;++i){
            int s,t;
            scanf("%d%d",&s,&t);
            int lc=lca(s,t);
            a1[s].push_back(dep[s]);//a1是加的桶,记录标记,dfs的时候再释放 
            a2[fa[lc][0]].push_back(dep[s]);//a2是减的桶 
            b1[t].push_back(dep[s]-2*dep[lc] +n);//防止越界 
            b2[lc].push_back(dep[s]-2*dep[lc] +n);
        }
        dfs2(1,0);
        for(ri i=1;i<=n;++i) printf("%d ",ans[i]);
    }
    /*
    6 3
    2 3
    1 2 
    1 4 
    4 5 
    4 6 
    0 2 5 1 2 3 
    1 5 
    1 3 
    2 6 
    
    5 3
    1 2 
    2 3 
    2 4 
    1 5 
    0 1 0 3 0 
    1 4
    3 1 
    5 5 
    */
    View Code
  • 相关阅读:
    OGG实验:喂奶间隔数据表通过OGG配置同步
    Oracle Exadata 学习笔记之核心特性Part1
    js 表格指定列,根据相同值实现跨行合并
    tr td同时添加点击事件
    Oracle 分页查询
    tomcat启动时运行指定的java类
    application/x-www-form-urlencoded与multipart/form-data与application/json的区别 精析
    nodejs中thiskeyword的问题
    AlphaGo 开源项目研究(1)
    LeetCode -- Best Time to Buy and Sell Stock II
  • 原文地址:https://www.cnblogs.com/mowanying/p/11808536.html
Copyright © 2011-2022 走看看