zoukankan      html  css  js  c++  java
  • NOIP2016 天天爱跑步

     

    转载请注明出处:

    http://www.cnblogs.com/hzoi-wangxh/p/7738626.html 

    天天爱跑步

    时间限制: 2 Sec  内存限制: 512 MB

    题目描述

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

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

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

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

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

    输入

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

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

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

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

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

    输出

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

    样例输入

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

    样例输出

    2 0 0 1 1 1


    solution:
            用deep表示每个节点的深度,w表示每个观察员观察的时间。
            先用lca处理出每一个玩家起点和终点的最近公共祖先,然后用动态开点线段树去解决。
            我们可以把每一个玩家分为两步处理:向上走的和向下走的。向上走如果会被观察员观察到,deep[s]-deep[i]=w[i],再导一步deep[s]=deep[i]+w[i]。所以我们可以对于每一个玩家差分,在起点的dfs序上加1,lca的dfs序上减1,。每一层建一棵线段树,由dfs序卡出每一个节点的子树。这样,当我们全部插入后,只要查询第deep[i]+w[i]这一个深度的线段树中leftdfs[i]到rightdfs[i]区间内的权值,就可以知道从下向上走的ans[i]。
            从上向下走的比较难处理。对于向下跑的,如果玩家能被观察者i观察到,deep[s]+deep[i]-2*deep[lca[s]]=w[i].移相,得到deep[s]-2*deep[lca[s]]=w[i]-deep[i]。所以插入时在深度为deep[s]-2*deep[lca[s]]的线段树里进行差分,deep[t]处加1,deep[lca]处减1.查询时询问深度为w[i]-deep[i]线段树区间即可。
           注意,1、由于深度较大,节点多,所以使用动态开点线段树能有效节约内存。
                       2、向下走的w[i]-deep[i]可能为负数,所以应集体向右平移一个定值,数组也要相应开大一点。
                       3、做完向上跑的玩家之后,清空线段树。
    附上代码:
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    struct tree{
        int u,v,next;
    }l[601000];
    struct tree2{
        int st,en,go;
    }ll[301000];
    int n,lian[301000],e,cnt,m,dep[301000],ldfn[301000],rdfn[301000];
    int lc[7501000],rc[7501000],w[301000],p[300100][22],root[7501000];
    int son[301000],size[301000],hh[7501000],fa[301000],an[300100],num;
    void bian(int,int);
    void dfs(int);
    void work();
    int lca(int,int);
    void change(int,int,int,int,int&);
    int search(int,int,int,int,int);
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            bian(x,y);
            bian(y,x);
        }
        for(int i=1;i<=n;i++)
            scanf("%d",&w[i]);
        dfs(1);
        work();
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&ll[i].st,&ll[i].en);
            ll[i].go=lca(ll[i].st,ll[i].en);
        }
        for(int i=1;i<=m;i++)
        {
            change(ldfn[ll[i].st],1,1,n,root[dep[ll[i].st]]);
            change(ldfn[fa[ll[i].go]],-1,1,n,root[dep[ll[i].st]]);
        }
        for(int i=1;i<=n;i++)
            an[i]+=search(ldfn[i],rdfn[i],1,n,root[dep[i]+w[i]]);
        memset(lc,0,sizeof(lc));
        memset(rc,0,sizeof(rc));
        memset(root,0,sizeof(root));
        cnt=0;
        memset(hh,0,sizeof(hh));
        for(int i=1;i<=m;i++)
        {
            change(ldfn[ll[i].en],1,1,n,root[dep[ll[i].st]-2*dep[ll[i].go]+2*n]);
            change(ldfn[ll[i].go],-1,1,n,root[dep[ll[i].st]-2*dep[ll[i].go]+2*n]);
        }
        for(int i=1;i<=n;i++)
            an[i]+=search(ldfn[i],rdfn[i],1,n,root[w[i]-dep[i]+2*n]);
        for(int i=1;i<=n;i++)
            printf("%d ",an[i]);
        return 0;
    }
    void bian(int x,int y)
    {
        e++;
        l[e].u=x;
        l[e].v=y;
        l[e].next=lian[x];
        lian[x]=e;
    }
    void dfs(int x)
    {
        ldfn[x]=++num;
        size[x]=1;
        for(int i=lian[x];i;i=l[i].next)
        {
            int v=l[i].v;
            if(v!=fa[x])
            {
                fa[v]=x;
                dep[v]=dep[x]+1;
                dfs(v);
                size[x]+=size[v];
                if(size[v]>size[son[x]])
                    son[x]=v;
            }
        }
        rdfn[x]=num;
    }
    void work()
    {
        for(int i=1;i<=n;i++)
            p[i][0]=fa[i];
        for(int i=1;i<=20;i++)
            for(int j=1;j<=n;j++)
                if(p[j][i-1]!=0)
                    p[j][i]=p[p[j][i-1]][i-1];
    }
    int lca(int x,int y)
    {
        if(dep[x]<dep[y])
            swap(x,y);
        int k=dep[x]-dep[y];
        for(int i=20;i>=0;i--)
            if(k-(1<<i)>=0)
            {
                k-=(1<<i);
                x=p[x][i];
            }
        if(x==y) return x;
        for(int i=20;i>=0;i--)
            if(p[x][i]!=0&&p[x][i]!=p[y][i])
            {
                x=p[x][i];
                y=p[y][i];
            }
        return fa[x];
    }
    void change(int x,int num,int le,int ri,int &now)
    {
        if(x==0) return;
        if(now==0)
            now=++cnt;
        hh[now]+=num;
        if(le==ri) return;
        int mid=(le+ri)>>1;
        if(x<=mid)
            change(x,num,le,mid,lc[now]);
        else
            change(x,num,mid+1,ri,rc[now]);
    }
    int search(int ll,int rr,int le,int ri,int now)
    {
        if(now==0)
            return 0;
        if(ll==le&&rr==ri)
            return hh[now];
        int mid=(le+ri)>>1;
        if(rr<=mid)
            return search(ll,rr,le,mid,lc[now]);
        else
            if(ll>mid)
                return search(ll,rr,mid+1,ri,rc[now]);
            else
                return search(ll,mid,le,mid,lc[now])+search(mid+1,rr,mid+1,ri,rc[now]);
    }
    动态开点线段树可以有很多打法,不一定像我一样打。

  • 相关阅读:
    ReportViewer,RDLC 报表开发之分页
    Mvc2.0 处理自定义错误.
    使用 WPS中粘贴VS里的代码,并整理格式
    Sql2008中添加程序集.
    快速整理列说明.SQL2008.
    ASP.NET MVC在IIS6下部署的小技巧
    MS SQL Server将数据导出Insert语句的存储过程
    在IE6下发生Internet Explorer cannot open the Internet site错误
    Windows7 中配置IIS7的方法(HTTP 错误 404.3 Not Found)
    安卓2.2手动开启APP2SD方法
  • 原文地址:https://www.cnblogs.com/hzoi-wangxh/p/7738626.html
Copyright © 2011-2022 走看看