zoukankan      html  css  js  c++  java
  • sss

    <更新提示>

    <第一次更新>


    <正文>

    天天爱跑步

    Description

    小C同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。《天天爱跑步》是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。

    这个游戏的地图可以看作一棵包含 n 个结点和 n−1 条边的树,每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从 1 到 n 的连续正整数。

    现在有 m 个玩家,第 i 个玩家的起点为 Si,终点为 Ti。每天打卡任务开始时,所有玩家在第 0 秒同时从自己的起点出发,以每秒跑一条边的速度,不间断地沿着最短路径向着自己的终点跑去,跑到终点后该玩家就算完成了打卡任务。(由于地图是一棵树,所以每个人的路径是唯一的)

    小C想知道游戏的活跃度,所以在每个结点上都放置了一个观察员。在结点 j 的观察员会选择在第 Wj 秒观察玩家,一个玩家能被这个观察员观察到当且仅当该玩家在第 Wj 秒也正好到达了结点 j。小C想知道每个观察员会观察到多少人?

    注意:我们认为一个玩家到达自己的终点后该玩家就会结束游戏,他不能等待一段时间后再被观察员观察到。即对于把结点 j 作为终点的玩家:若他在第 Wj 秒到达终点,则在结点 j 的观察员不能观察到该玩家;若他正好在第 Wj 秒到达终点,则在结点 j 的观察员可以观察到这个玩家。

    Input Format

    从标准输入读入数据。

    第一行有两个整数 n 和 m。其中 n 代表树的结点数量,同时也是观察员的数量,m 代表玩家的数量。

    接下来 n−1 行每行两个整数 u 和 v,表示结点 u 到结点 v 有一条边。

    接下来一行 n 个整数,其中第 j 个整数为 Wj,表示结点 j 出现观察员的时间。

    接下来 m 行,每行两个整数 Si 和 Ti,表示一个玩家的起点和终点。

    对于所有的数据,保证 1≤Si,Ti≤n,0≤Wj≤n。

    Output Format

    输出到标准输出。

    输出 1 行 n 个整数,第 j 个整数表示结点 j 的观察员可以观察到多少人。

    Sample Input

    6 3
    2 3
    1 2
    1 4
    4 5
    4 6
    0 2 5 1 2 3
    1 5
    1 3
    2 6
    

    Sample Output

    2 0 0 1 1 1
    

    解析

    这已经是经典题了,其实也没那么难。

    先把玩家跑步的路径用(LCA)拆成两段,当然是(S_i-p)(p-T_i)(p)(S_i)(T_i)的最近公共祖先。那么,在前半条路径上,满足(d[S_i]-d[x]=w[x])的点(x)就会有(1)的贡献,在后半条路径上,满足(d[S_i]+d[x]-2*d[p]=w[x])的点(x)也会有(1)的贡献,这可以用行走时间来理解。

    于是想到换式子:(d[S_i]=w[x]+d[x])(d[S_i]-2*d[p]=w[x]-d[x]),发现可以转换问题:从(S_i)(p)的路径上,在每个点放物品(d[S_i]),求每个点(x)处物品(w[x]+d[x])有几个,从(p)(T_i)的路径上,在每个点放物品(d[S_i]-2*d[p]),求每个点(x)处物品(w[x]-d[x])有几个。

    做一下树上差分,将路径修改变为单点修改,然后我们发现可以值域线段树开桶,线段树合并求一下子树和,线段树查询统计答案。

    当然,有更简单的方法:将单点加减操作记录在每个节点的(vector)里,然后一边(dfs)一边做修改,由于计数统计具有区间可减性,所以回溯时的答案减掉刚遍历时的答案就是真正的子树和。

    (Code:)

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 300020;
    struct edge { int ver,next; } e[N*2];
    int n,m,tot,Head[N],w[N],s[N],t[N],c[N*3],ans[N];
    int dep[N],fa[N],son[N],size[N],top[N];
    vector < pair<int,int> > re1[N],re2[N];
    inline void insert(int x,int y)
    {
        e[++tot] = (edge){y,Head[x]} , Head[x] = tot;
    }
    inline void input(void)
    {
        scanf("%d%d",&n,&m);
        for ( int i = 1; i < n; i++ )
        {
            int u,v; scanf("%d%d",&u,&v);
            insert( u , v ) , insert( v , u );
        }
        for ( int i = 1; i <= n; i++ )
            scanf("%d",&w[i]);
        for ( int i = 1; i <= m; i++ )
            scanf("%d%d",&s[i],&t[i]);
    }
    inline void dfs1(int x,int f,int depth)
    {
        fa[x] = f , dep[x] = depth , size[x] = 1;
        int Max = -1;
        for ( int i = Head[x]; i; i = e[i].next )
        {
            int y = e[i].ver;
            if ( y == f ) continue;
            dfs1( y , x , depth+1 );
            size[x] += size[y];
            if ( size[y] > Max ) Max = size[y] , son[x] = y;
        }
    }
    inline void dfs2(int x,int Top)
    {
        top[x] = Top;
        if ( !son[x] ) return;
        else dfs2( son[x] , Top );
        for ( int i = Head[x]; i; i = e[i].next )
        {
            int y = e[i].ver;
            if ( y == fa[x] || y == son[x] ) continue;
            dfs2( y , y );
        }
    }
    inline int LCA(int x,int y)
    {
        while ( top[x] ^ top[y] )
        {
            if ( dep[top[x]] < dep[top[y]] ) swap( x , y );
            x = fa[top[x]];
        }
        return dep[x] < dep[y] ? x : y;
    }
    inline void initl(void)
    {
        for ( int i = 1; i <= m; i++ )
        {
            int p = LCA( s[i] , t[i] );
            re1[s[i]].push_back( make_pair(dep[s[i]],1) );
            re1[fa[p]].push_back( make_pair(dep[s[i]],-1) );
        }
    }
    inline void dfsl(int x)
    {
        int cnt = c[ w[x] + dep[x] ];
        for ( int i = 0; i < re1[x].size(); i++ )
            if ( re1[x][i].second == 1 )
                c[ re1[x][i].first ] ++;
            else c[ re1[x][i].first ] --;
        for ( int i = Head[x]; i; i = e[i].next )
        {
            int y = e[i].ver;
            if ( y == fa[x] ) continue;
            dfsl( y );
        }
        ans[x] += c[ w[x] + dep[x] ] - cnt;
    }
    inline void initr(void)
    {
        for ( int i = 1; i <= m; i++ )
        {
            int p = LCA( s[i] , t[i] );
            re2[t[i]].push_back( make_pair(dep[s[i]]-2*dep[p],1) );
            re2[p].push_back( make_pair(dep[s[i]]-2*dep[p],-1) );
        }
    }
    inline void dfsr(int x)
    {
        int cnt = c[ w[x] - dep[x] + n ];
        for ( int i = 0; i < re2[x].size(); i++ )
            if ( re2[x][i].second == 1 )
                c[ re2[x][i].first + n ] ++;
            else c[ re2[x][i].first + n ] --;
        for ( int i = Head[x]; i; i = e[i].next )
        {
            int y = e[i].ver;
            if ( y == fa[x] ) continue;
            dfsr( y );
        }
        ans[x] += c[ w[x] - dep[x] + n ] - cnt;
    }
    int main(void)
    {
        input();
        dfs1( 1 , 0 , 1 );
        dfs2( 1 , 1 );
        initl();
        dfsl( 1 );
        initr();
        dfsr( 1 );
        for ( int i = 1; i <= n; i++ )
            printf("%d ",ans[i]);
        puts("");
        return 0;
    }
    

    <后记>

  • 相关阅读:
    HDU 1863 畅通工程(Kruskal)
    HDU 1879 继续畅通工程(Kruskra)
    HDU 1102 Constructing Roads(Kruskal)
    POJ 3150 Cellular Automaton(矩阵快速幂)
    POJ 3070 Fibonacci(矩阵快速幂)
    ZOJ 1648 Circuit Board(计算几何)
    ZOJ 3498 Javabeans
    ZOJ 3490 String Successor(模拟)
    Java实现 LeetCode 749 隔离病毒(DFS嵌套)
    Java实现 LeetCode 749 隔离病毒(DFS嵌套)
  • 原文地址:https://www.cnblogs.com/Parsnip/p/11210069.html
Copyright © 2011-2022 走看看