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

    时间限制:2 s   内存限制: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的观察员可以观察到多少人。

    【样例1输入】

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

    【样例1输出】

    2 0 0 1 1 1

    【样例2输入】

    5 3

    1 2

    2 3

    2 4

    1 5

    0 1 0 3 0

    3 1

    1 4

    5 5

    【样例2输出】

    1 2 1 0 1

    【提示】

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

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

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

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

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

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

      如果你的程序需要用到较大的栈空间(这通常意味着需要较深层数的递归),请务必仔细阅读选手目录下的文档running/stackpdf,以了解在最终评测时栈空间的限制与在当前工作环境下调整栈空间限制的方法。

    【题解】

            NOIP2016Day1T2,NOIP,Day1,T2……当年在考场上对学长们是很大的考验,一年后依然感受到了沉重的压力,不仅来自这道一年前的NOIP2016,恐怕更来自NOIP2017吧。不过轻松些说,这道题的暴力倒是也很好写,总不像省选题连暴力分都不那么好拿,其实还是比较蒟蒻友好的。虽然说是NOIP但是出什么题也不一定,到了考场上只有自己拿主意啊。

           在树上,路径唯一,特别关键的一个参考就是LCA。可以知道链的情况是比较好做的,那么如果把一段路径从LCA处断开不就拆出两条链了吗。处理出LCA拆出链来(终于有一道适合用离线tarjan做的题了2333)。一个点在从S到LCA的路径上,只有dep[s]-dep[i]=w[i]的时候才观测得到,而把dep[i]移项后右侧就变成了一个只和i有关的式子,左侧可以看做给出的条件。在LCA到T的路径上则是dep[t]-dep[i]=len-w[i],这里的len=dep[s]+dep[t]-2*dep[LCA]。问题就变成了统计每个点子树中dep满足要求的点的数量。一遍dfs确定父子关系和预处理深度,第二遍处理s到lca,第三遍处理lca到t。用一个数组记录一下每一深度的s有多少个,用开始遍历该点得到的值减去遍历该点之后的值,得到的就是该点子树里满足要求的点的个数,第三遍同理。对于这道题来说想到拆链才是最重要的吧,否则只是从表面上看根本看不出来应该怎么做;但是部分分里面也相当于给了些提示,读题还是第一要紧之事啊。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<vector>
    using namespace std;
    const int sj=300000;
    int n,m,h[sj],e,a1,a2,dep[sj],wi[sj],jg[sj],t1[sj],t2[sj*3],len[sj];
    int zb[sj],yb[sj],l[sj],e1,fa[sj],zx[sj],deep,val[sj],ans[sj];
    bool r[sj];
    vector<int> mb1[sj],mb2[sj],mb3[sj];
    struct B
    {
        int ne,v;
    }b[sj<<1];
    struct Q
    {
        int ne,v,num;
    }q[sj<<1];
    void add(int x,int y)
    {
        b[e].v=y;
        b[e].ne=h[x];
        h[x]=e++;
    }
    void adq(int x,int y,int z)
    {
        q[e1].v=y;
        q[e1].ne=l[x];
        q[e1].num=z;
        l[x]=e1++;
    }
    void dfs(int x,int fx)
    {
        dep[x]=dep[fx]+1;
        if(dep[x]>deep) deep=dep[x];
        for(int i=h[x];i!=-1;i=b[i].ne)
          if(b[i].v!=fx)
            dfs(b[i].v,x);
    }
    int find(int x)
    {
        if(fa[x]==-1) return x;
        fa[x]=find(fa[x]);
        return fa[x];
    }
    void hb(int x,int y)
    {
        x=find(x),y=find(y);
        if(x!=y) fa[x]=y;
    }
    void lca(int x)
    {
         r[x]=1;
         zx[x]=x;
         for(int i=h[x];i!=-1;i=b[i].ne)
           if(!r[b[i].v])
           {
              lca(b[i].v);
              hb(x,b[i].v);
              zx[find(b[i].v)]=x;
           }
         for(int i=l[x];i!=-1;i=q[i].ne)
           if(r[q[i].v])
             jg[q[i].num]=zx[find(q[i].v)];
    }
    void dfs2(int x,int fx)
    {
         int now=wi[x]+dep[x],cun;
         if(now<=deep) cun=t1[now];
         for(int i=h[x];i!=-1;i=b[i].ne)
           if(b[i].v!=fx) 
             dfs2(b[i].v,x);
         t1[dep[x]]+=val[x];
         if(now<=deep) ans[x]=t1[now]-cun;
         for(int i=0,ss=mb1[x].size();i<ss;i++) t1[dep[mb1[x][i]]]--;
    }
    void dfs3(int x,int fx)
    {
         int now=dep[x]-wi[x]+sj,cun; 
         cun=t2[now];
         for(int i=h[x];i!=-1;i=b[i].ne)
           if(b[i].v!=fx)
             dfs3(b[i].v,x);
         for(int i=0,ss=mb2[x].size();i<ss;i++)  t2[sj+mb2[x][i]]++;
         ans[x]+=t2[now]-cun;
         for(int i=0,ss=mb3[x].size();i<ss;i++) t2[sj+mb3[x][i]]--;
    }
    int main()
    {
        int __size__=64<<20; 
        char *__p__=(char*)malloc(__size__)+__size__;  
        __asm__("movl %0, %%esp
    "::"r"(__p__));  
        scanf("%d%d",&n,&m);
        memset(h,-1,sizeof(h));
        memset(l,-1,sizeof(l));
        memset(fa,-1,sizeof(fa));
        for(int i=1;i<n;i++)
        {
           scanf("%d%d",&a1,&a2);
           add(a1,a2),add(a2,a1);
        }
        for(int i=1;i<=n;i++) scanf("%d",&wi[i]);
        for(int i=1;i<=m;i++)
        {
           scanf("%d%d",&zb[i],&yb[i]);
           val[zb[i]]++;
           adq(zb[i],yb[i],i),adq(yb[i],zb[i],i);
        }
        dfs(1,0);
        lca(1);
        for(int i=1;i<=m;i++) 
        {
          mb1[jg[i]].push_back(zb[i]);
          len[i]=dep[zb[i]]+dep[yb[i]]-2*dep[jg[i]];
          mb2[yb[i]].push_back(dep[yb[i]]-len[i]);
          mb3[jg[i]].push_back(dep[yb[i]]-len[i]);
        }
        dfs2(1,0);
        dfs3(1,0);
        for(int i=1;i<=m;i++)
          if(dep[zb[i]]-dep[jg[i]]==wi[jg[i]])
             ans[jg[i]]--;
        for(int i=1;i<n;i++) printf("%d ",ans[i]);
        printf("%d",ans[n]);
        return 0;
    }
    running
  • 相关阅读:
    调用外部程序主窗体做子窗体
    查看window编码
    c# 数据库更新和界面刷新的问题
    c# datagridview代码(网上的)
    winform DataGridView控件的打印
    西电ubuntu更新软件源
    C++ primer 学习笔记(2):函数
    C++ Primer 学习笔记(1)——迭代器,数组
    查询表属于哪个数据
    oracle 实用语句
  • 原文地址:https://www.cnblogs.com/moyiii-/p/7360378.html
Copyright © 2011-2022 走看看