zoukankan      html  css  js  c++  java
  • BZOJ 4719--天天爱跑步(LCA&差分)

    4719: [Noip2016]天天爱跑步

    Time Limit: 40 Sec  Memory Limit: 512 MB
    Submit: 1464  Solved: 490
    [Submit][Status][Discuss]

    Description

    小c同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。?天天爱跑步?是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。这个游戏的地图可以看作一一棵包含 N个结点和N-1 条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从1到N的连续正整数。现在有个玩家,第个玩家的起点为Si ,终点为Ti  。每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发, 以每秒跑一条边的速度,不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务。 (由于地图是一棵树, 所以每个人的路径是唯一的)小C想知道游戏的活跃度, 所以在每个结点上都放置了一个观察员。 在结点的观察员会选择在第Wj秒观察玩家, 一个玩家能被这个观察员观察到当且仅当该玩家在第Wj秒也理到达了结点J  。 小C想知道每个观察员会观察到多少人?注意: 我们认为一个玩家到达自己的终点后该玩家就会结束游戏, 他不能等待一 段时间后再被观察员观察到。 即对于把结点J作为终点的玩家: 若他在第Wj秒重到达终点,则在结点J的观察员不能观察到该玩家;若他正好在第Wj秒到达终点,则在结点的观察员可以观察到这个玩家

    Input

    第一行有两个整数N和M 。其中N代表树的结点数量, 同时也是观察员的数量, M代表玩家的数量。
    接下来n-1 行每行两个整数U和V ,表示结点U 到结点V 有一条边。
    接下来一行N 个整数,其中第个整数为Wj , 表示结点出现观察员的时间。
    接下来 M行,每行两个整数Si和Ti,表示一个玩家的起点和终点。
    对于所有的数据,保证 1<=Si,Ti<=N,0<=Wj<=N
     

    Output

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

     

    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

    HINT


    对于1号点,W1=0,故只有起点为1号点的玩家才会被观察到,所以玩家1和玩家2被观察到,共2人被观察到。

    对于2号点,没有玩家在第2秒时在此结点,共0人被观察到。

    对于3号点,没有玩家在第5秒时在此结点,共0人被观察到。

    对于4号点,玩家1被观察到,共1人被观察到。

    对于5号点,玩家1被观察到,共1人被观察到。

    对于6号点,玩家3被观察到,共1人被观察到
     

    题目链接:

        http://www.lydsy.com/JudgeOnline/problem.php?id=4719 

    Solution

        刚开始学OI的时候就看过这道题。。。当时是贴代码过的。。。。

        记得好像是用树链剖分。。不记得了。。反正很长。。。

        现在快要noip了。。。把往年的题目拿出来看一下,算是临近noip的梳理吧。。。。。

        好多废话

        进入正题。。。。。

        首先每个人都要走最短路径。。。马上想到LCA。。。不然还能有什么。。。

        每个人分别统计显然是不行的,不过似乎可以根据特殊情况水很多分。。

        于是想到差分。。

        假设第 i 个人的起点为 Si ,终点为 Ti 。。LCA ( Si ,Ti ) =  rt  

        每条线段长度都是 1 ,故路径长度与深度有关。。。

        于是路径就可以分成两部分:Si -> rt  和 rt ->Ti

        两条路径分开统计。。。

        在向上的路径中,比如一个起点 Si ,深度为 dep [ Si ] ,

        那么可以在遍历到Si的时候 U [ dep [ Si ] ] ++;

        然后在到达 rt 的父亲的时候 U [ dep [ Si ] ] --;  (U [ i ]表示某一方向路径的总值)

        向下的路径用一样的方法。。。但这样rt 的统计会有重复。。

        所以两次统计中要有一次在 rt 的时候就 U [ dep [ Si ] ] - - ;

        这样就可以了,虽然分析很长但代码不是很长。。。。

        注意在BZOJ上提交不能有末尾空格。。。害我PE了一发。。。

    代码

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<iostream>
    #define N 400000
    using namespace std;
    inline int Read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    int n,m,cnt=0;
    int fa[N][22];
    int hed[N],w[N],dep[N],ans[N],U[N],D[N<<1];
    struct edge{
        int r,nxt;
    }e[N<<1];
    struct node{
        int u,w;
    };
    vectore1[N],e2[N<<1];
    void insert(int u,int v){
        e[++cnt]=(edge){v,hed[u]};hed[u]=cnt;
        e[++cnt]=(edge){u,hed[v]};hed[v]=cnt;
    }
    void dfs1(int x,int F){
        for(int i=1;(1<<i)<=dep[x];i++)
            fa[x][i]=fa[fa[x][i-1]][i-1];
        for(int i=hed[x];i;i=e[i].nxt)
            if(e[i].r!=F){
                dep[e[i].r]=dep[x]+1;
                fa[e[i].r][0]=x;
                dfs1(e[i].r,x);
            }
    }
    int lca(int u,int v){
        if(dep[u]<dep[v])swap(u,v);
        int d=dep[u]-dep[v];
        for(int i=0;(1<<i)<=d;i++)
            if((1<<i)&d)
                u=fa[u][i];
        if(u==v)return u;
        for(int i=20;i>=0;i--){
            if((1<<i)>dep[u] || fa[u][i]==fa[v][i])continue;
            u=fa[u][i];v=fa[v][i];
        }
        return fa[u][0];
    }
    void dfs2(int x,int F){
        ans[x]-=U[w[x]+dep[x]];
        ans[x]-=D[w[x]-dep[x]+n];
        for(int i=0;i<e1[x].size();i++)
            U[e1[x][i].u]+=e1[x][i].w;
        for(int i=0;i<e2[x].size();i++)
            D[e2[x][i].u+n]+=e2[x][i].w;
        for(int i=hed[x];i;i=e[i].nxt)
            if(e[i].r!=F)
                dfs2(e[i].r,x);
        ans[x]+=U[w[x]+dep[x]]+D[w[x]-dep[x]+n];
    }
    int main(){
        int u,v,rt;
        n=Read();m=Read();
        for(int i=1;i<n;i++){
            u=Read();v=Read();
            insert(u,v);
        }
        for(int i=1;i<=n;i++)
            w[i]=Read();
        dfs1(1,0);
        for(int i=1;i<=m;i++){
            u=Read();v=Read();
            rt=lca(u,v);
            e1[u].push_back((node){dep[u],1});
            e1[rt].push_back((node){dep[u],-1});
            e2[v].push_back((node){dep[u]-(dep[rt]<<1),1});
            e2[fa[rt][0]].push_back((node){dep[u]-(dep[rt]<<1),-1});
        }
        dfs2(1,0);
        for(int i=1;i<n;i++)
            printf("%d ",ans[i]);
        printf("%d",ans[n]);
        return 0;
    }
    

      

      

    This passage is made by Iscream-2001.

  • 相关阅读:
    D. Constructing the Array
    B. Navigation System
    B. Dreamoon Likes Sequences
    A. Linova and Kingdom
    G. Special Permutation
    B. Xenia and Colorful Gems
    Firetrucks Are Red
    java getInstance()的使用
    java 静态代理和动态代理
    java 类加载机制和反射机制
  • 原文地址:https://www.cnblogs.com/Yuigahama/p/7767829.html
Copyright © 2011-2022 走看看