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

    [NOIP2016]天天爱跑步

    题目

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

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

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

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

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

    INPUT

    第一行有两个整数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

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

    SAMPLE

    INPUT1

    6 3

    2 3

    1 2

    1 4

    4 5

    4 6

    0 2 5 1 2 3

    1 5

    1 3 

    2 6

    OUTPUT1

    2 0 0 1 1 1

    INPUT2

    5 3

    1 2

    2 3

    2 4

    1 5

    0 1 0 3 0

    3 1

    1 4

    5 5

    OUTPUT2

    1 2 1 0 1

    范围

    解题报告

    从今天下午做到晚上,终于A了= =(明明是从去年做到现在吧啊喂)

    设$deep[]$表示节点的深度,$watch[]$表示观察者的出现时间,$s$表示玩家起点,$t$表示终点

    首先,只有满足 观察者出现时间=玩家起点与观察者距离  的玩家才对观察者有贡献

    显然,在树上的问题,我们很容易想到把路径拆成从起点向上跑到$lca$的路径和$lca$的子节点向下跑到终点的两条路径

    对于向上跑的,如果玩家能被观察者$i$观察到,那么一定满足:

    $$deep[s]-deep[i]=watch[i]$$

    对于向下跑的,如果玩家能被观察者$i$观察到,那么一定满足:

    $$deep[s]+deep[i]-2 imes deep[lca(s,i)]=watch[i]$$

    (显然这两个式子都是由“观察者出现时间=玩家起点与观察者距离”推出来的)

    等号左边是玩家起点与观察者的距离,等号右边是观察者出现时间

    我们先分析向上跑的——路径:从起点向上跑到$lca$

    将式子移项可以得到:

    $$deep[s]=deep[i]+watch[i]$$

    我们知道$deep[i]$与$watch[i]$都是定值,也就是说,对于观察者$i$,他能观察到的向上跑的玩家,是所有起点=$deep[i]+watch[i]$的玩家

    而向上跑还能经过$i$的,一定在$i$的子树中

    所以,我们知道,以$i$为根的子树中,所以深度为$deep[i]+watch[i]$的玩家都能被$i$观察到

    那么我们搞一个$dfs$序,就能把树上的问题转化为区间上的问题了

    深度怎么处理呢?

    我们对每一层深度建一棵线段树(此时我们需要动态开点(第一棵动态开点线段树,快打死我了)),那么剩下的就很好解决了

    我们将问题转化为了在深度为$deep[i]+watch[i]$的线段树中,查询区间$[a,b]$的玩家个数

    玩家个数自然很好处理,瞎XX差分一下就可以了

    差分:

    在起点处$+1$

    在$lca$父节点处$-1$

    为什么?

    这是差分的惯用思想(科普差分):

    我们考虑一个大区间,一开始我们全赋为0,然后我们插入一个区间,将左端点$+1$,右端点后面一个点$-1$,那么在该区间前面的点,显然不受影响,在该区间中的点,我们进行求前缀和处理,我们只能求到左端点的$+1$,而不会求到右端点的$-1$,所以该区间可以被算上,在该区间后的点,我们再求一下前缀和,左端点的$+1$与右端点的$-1$相抵消,对其没有影响(在计算时,它也不会被算在该区间里)

    从$lca$的子节点向下跑到终点的同理

    把式子移项可得:

    $$deep[s]-2 imes deep[lca(s,i)]=watch[i]-deep[i]$$

    然后$watch[i]$和$deep[i]$仍然是定值,所以我们在深度为$deep[s]-2 imes deep[lca(s,i)]$的线段树里进行差分,终点处$+1$,$lca$处$-1$

    查询深度为$watch[i]-deep[i]$的线段树即可

    -----update-----

     动态开点线段树:

    注意到,我们在每个深度都开了一棵线段树,所以,我们不可能像以前一样,直接开一大圈线段树,那样内存一定会炸,所以我们可以先开出足够的数组,然后,当我们需要一棵新的线段树时,只需要增加一个新的根节点即可,同样的,当我们需要一个新节点时,我们不需要像以前打线段树一样,去找父节点编号$ imes 2$什么的,我们直接在最后开出的节点后面加一个节点,并将它的父节点的儿子指针指向该节点,这样,就可以省去那些一言不合就$ imes 2$的下标了,同样,也可以省去许多会让你$MLE$的内存

    -----update over-----

    细节处理:

    1. 做完向上跑的玩家后,清空线段树
    2. 向下跑的$deep[s]-2 imes deep[lca(s,i)]$可能出负数,所以全体后移,数组大小要开大一些
    3. 不会动态开点的先学习一下,不要像我一样作死的以为随便打打就好了= =
      1 #include<iostream>
      2 #include<cstring>
      3 #include<cstdio>
      4 using namespace std;
      5 inline int read(){
      6     int sum(0);
      7     char ch(getchar());
      8     for(;ch<'0'||ch>'9';ch=getchar());
      9     for(;ch>='0'&&ch<='9';sum=sum*10+(ch^48),ch=getchar());
     10     return sum;
     11 }
     12 struct edge{
     13     int e;
     14     edge *n;
     15     edge():e(0),n(NULL){}
     16 }a[600005],*pre[300005];
     17 int tot;
     18 inline void insert(int s,int e){
     19     a[++tot].e=e;
     20     a[tot].n=pre[s];
     21     pre[s]=&a[tot];
     22 }
     23 struct que{
     24     int s,e,lca;
     25 }peo[300005];
     26 int n,m;
     27 int cnt;
     28 int dep[300005],fa[20][300005],id[300005],out[300005];
     29 inline void dfs(int u){
     30     id[u]=++cnt;
     31     for(int i=1;(1<<i)<=dep[u];++i)
     32         fa[i][u]=fa[i-1][fa[i-1][u]];
     33     for(edge *i=pre[u];i;i=i->n){
     34         int e(i->e);
     35         if(e!=fa[0][u]){
     36             fa[0][e]=u;
     37             dep[e]=dep[u]+1;
     38             dfs(e);
     39         }
     40     }
     41     out[u]=cnt;
     42 }
     43 inline int lca(int x,int y){
     44     if(dep[x]<dep[y])
     45         swap(x,y);
     46     int delta(dep[x]-dep[y]);
     47     for(int i=0;delta>0;++i)
     48         if((1<<i)&delta){
     49             delta^=1<<i;
     50             x=fa[i][x];
     51         }
     52     if(x==y)
     53         return x;
     54     for(int i=19;i>=0;--i)
     55         if(fa[i][x]!=fa[i][y])
     56             x=fa[i][x],y=fa[i][y];
     57     return fa[0][x];
     58 }
     59 int dfn;
     60 int sum[7500005],lc[7500005],rc[7500005],root[1200005];
     61 inline void update(int l,int r,int pos,int w,int &now){
     62     if(!pos)
     63         return;
     64     if(!now)
     65         now=++dfn;
     66     sum[now]+=w;
     67     if(l==r)
     68         return;
     69     int mid((l+r)>>1);
     70     if(pos<=mid)
     71         update(l,mid,pos,w,lc[now]);
     72     else
     73         update(mid+1,r,pos,w,rc[now]);
     74 }
     75 inline int query(int l,int r,int ll,int rr,int now){
     76     if(!now)
     77         return 0;
     78     if(ll<=l&&r<=rr)
     79         return sum[now];
     80     int mid((l+r)>>1);
     81     int ret(0);
     82     if(rr<=mid)
     83         return query(l,mid,ll,rr,lc[now]);
     84     else
     85         if(ll>mid)
     86             return query(mid+1,r,ll,rr,rc[now]);
     87         else
     88             return query(l,mid,ll,mid,lc[now])+query(mid+1,r,mid+1,rr,rc[now]);
     89 /*    if(ll<=mid)
     90         ret+=query(l,mid,ll,rr,lc[now]);
     91     if(mid<rr)
     92         ret+=query(mid+1,r,ll,rr,rc[now]);*/
     93 }
     94 inline void clear(){
     95     dfn=0;
     96     memset(lc,0,sizeof(lc));
     97     memset(rc,0,sizeof(rc));
     98     memset(sum,0,sizeof(sum));
     99     memset(root,0,sizeof(root));
    100 }
    101 int ans[300005];
    102 int w[300005];
    103 inline int gg(){
    104     freopen("runninga.in","r",stdin);
    105     freopen("runninga.out","w",stdout);
    106     memset(pre,NULL,sizeof(pre));
    107     n=read(),m=read();
    108     for(int i=1;i<n;++i){
    109         int x(read()),y(read());
    110         insert(x,y),insert(y,x);
    111     }
    112     dfs(1);
    113     for(int i=1;i<=n;++i)
    114         w[i]=read();
    115     for(int i=1;i<=m;++i)
    116         peo[i].s=read(),peo[i].e=read(),peo[i].lca=lca(peo[i].s,peo[i].e);
    117     for(int i=1;i<=m;++i){
    118         int de(dep[peo[i].s]);
    119         update(1,n,id[peo[i].s],1,root[de]);
    120         update(1,n,id[fa[0][peo[i].lca]],-1,root[de]);
    121     }
    122     for(int i=1;i<=n;++i)
    123         ans[i]+=query(1,n,id[i],out[i],root[dep[i]+w[i]]);
    124     clear();
    125     for(int i=1;i<=m;++i){
    126         int de(dep[peo[i].s]-(dep[peo[i].lca]<<1)+(n<<1));
    127         update(1,n,id[peo[i].e],1,root[de]);
    128         update(1,n,id[peo[i].lca],-1,root[de]);
    129     }
    130     for(int i=1;i<=n;++i)
    131         ans[i]+=query(1,n,id[i],out[i],root[w[i]-dep[i]+(n<<1)]);
    132     for(int i=1;i<=n;++i)
    133         printf("%d ",ans[i]);
    134     return 0;
    135 }
    136 int K(gg());
    137 int main(){;}
    View Code

    花絮?

    我:哇,为什么全WA了

    同桌:哪有全WA,你看还有15个T呢

    我:去去去,我从去年做到现在,去年还能拿点分,为啥现在连分都拿不了了

    同桌:哇,我A了5个点

    我:剩下的呢

    同桌:全T了

    我:快拿cena跑一跑

    同桌:哇,我A了

    我:多快?

    同桌:100秒= =

    (一下午就这样过去了)

    我:哇,我A了,你还差多少

    同桌:还差一点

    (几分钟过去了)

    我:哇,你要A了

    同桌:咦,怎么WA了一个

    我:是不是数组开小了

    同桌:唔,好像是,我回去开大点

    (又是几分钟过去了)

    我:A了?

    同桌:嗯

    我:咋WA的

    同桌:数组开小了

    我:那咋WA那么多次

    同桌:我不知道动态开点咋搞啊QAQ

    然后就有了上面的细节处理= =

  • 相关阅读:
    ajax翻页效果模仿yii框架
    一个伪ajax图片上传代码的例子
    php下intval()和(int)转换有哪些区别
    php中iconv函数使用方法
    php字符串截取问题
    ASP.net UrlRewrite的防盗链功能
    ASP.NET中application对象
    javascript回车完美实现tab切换功能
    有关c#装箱和拆箱知识整理
    PHP四大安全策略
  • 原文地址:https://www.cnblogs.com/hzoi-mafia/p/7354695.html
Copyright © 2011-2022 走看看