• [NOIP2016] 天天爱跑步 解题报告


    题意

    一棵 (n) 个节点的树, 树的每个节点上有一个观察员, 每一个观察员的观察时刻为 (time_i),

    (m) 个玩家, 每个玩家在 (0) 时刻时从起点 (s_i) 开始跑步, 每时刻经过一个节点, 沿着最短路径向终点 (t_i) 跑去.

    求每个观察员能观察到的玩家数量.

    思路

    暴力枚举每个玩家的行进路线一定是 (O(n^2)) 的, 不可行,
    那么考虑枚举每一个观察员, 求它能观察到多少个选手.

    若观察员 (x) 位于 (s_i)(lca_i) 的路径上, 那么 (x) 能观察到 (i) 的条件为

    [time_x = dep_{s_i} - dep_x ]

    转化一下变为,

    [time_x+dep_x = dep_{s_i} ]

    (x) 位于 (t_i)(lca_i) 的路径上, 那么 (x) 能观察到 (i) 的条件为

    [time_x = dis_{s_i o t_i} - (dep_t - dep_x) ]

    转化,

    [time_x - dep_x = dis - dep_t = dep_s - 2*dep_{lca_i} ]


    看起来树上差分, 枚举每个点的子树, 开个桶记录就好了. 但有个问题, 就是之前的桶会影响当前枚举到的点.
    我的做法比较蠢, 先把整棵树按照 $dfs$ 序排成一个序列, 并建立树状数组, 然后从小到大枚举差分值, 每次把所有差分值相同的点在 $dfs$ 序列上修改, 并让所有满足这个值的观察员进行查询.
    其实我们考虑一下, 当前点需要的并不是整个桶, 只需要它自己的两个值上面的值就行了. 所以我们枚举到每个点的时候先记录一下它的两个值, 然后枚举完它的子树后, 再查看它的两个值, 求个差就行了.

    代码

    (O(nlog n))

    #include<bits/stdc++.h>
    #define pb push_back
    #define sz size
    using namespace std;
    const int N=299998+7;
    const int L=20;
    int n,m,dep[N],dfn[N],en[N],cnt,tme[N],f[N][L+7],ans[N],s[N],t[N],lca[N],qes[N],poi[N];
    int t1[N],t2[N],c[N];
    int lst[N],nxt[2*N],to[2*N],tot;
    vector<int> p[N];
    void add(int x,int y){ nxt[++tot]=lst[x]; to[tot]=y; lst[x]=tot; }
    void pre(int u,int fa){
      dep[u]=dep[fa]+1;
      dfn[u]=++cnt;
      f[u][0]=fa;
      for(int i=1;i<=L;i++)
        f[u][i]=f[f[u][i-1]][i-1];
      for(int i=lst[u];i;i=nxt[i]){
        int v=to[i];
        if(v==fa) continue;
        pre(v,u);
      }
      en[u]=cnt;
    }
    int Lca(int x,int y){
      if(dep[x]<dep[y]) swap(x,y);
      for(int i=L;i>=0;i--)
        if(dep[f[x][i]]>=dep[y])
          x=f[x][i];
      if(x==y) return x;
      for(int i=L;i>=0;i--)
        if(f[x][i]!=f[y][i]){
          x=f[x][i];
          y=f[y][i];
        }
      return f[x][0];
    }
    bool rulea(int a,int b){ return tme[a]+dep[a]<tme[b]+dep[b]; }
    bool ruleb(int a,int b){ return tme[a]-dep[a]<tme[b]-dep[b]; }
    bool rule1(int a,int b){ return t1[a]<t1[b]; }
    bool rule2(int a,int b){ return t2[a]<t2[b]; }
    void modify(int x,int w){
      if(!x) return;
      for(int i=x;i<=n;i+=i&-i){
        c[i]+=w;
      }}
    int sum(int x){
      if(!x) return 0;
      int res=0;
      for(int i=x;i;i-=i&-i)
        res+=c[i];
      return res;
    }
    int query(int l,int r){
      return sum(r)-sum(l-1);
    }
    int main(){
      //freopen("run1.in","r",stdin);
      //freopen("run.out","w",stdout);
      cin>>n>>m;
      int x,y;
      for(int i=1;i<n;i++){
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
      }
      pre(1,0);
      for(int i=1;i<=n;i++){ qes[i]=i; scanf("%d",&tme[i]); }
      for(int i=1;i<=m;i++){
        scanf("%d%d",&s[i],&t[i]);
        lca[i]=Lca(s[i],t[i]);
        poi[i]=i;
        t1[i]=dep[s[i]];
        t2[i]=dep[s[i]]-2*dep[lca[i]];
      }
      sort(qes+1,qes+1+n,rulea);
      sort(poi+1,poi+1+m,rule1);
      int now=1;
      for(int i=1;i<=n;i++){
        int tmp=tme[qes[i]]+dep[qes[i]];
        while(now<=m&&t1[poi[now]]<tmp) now++;
        int lt=now;
        for(;now<=m&&t1[poi[now]]==tmp;now++){
          modify(dfn[s[poi[now]]],1);
          modify(dfn[f[lca[poi[now]]][0]],-1);
        }
        while(i<=n&&tme[qes[i]]+dep[qes[i]]==tmp){ ans[qes[i]]+=query(dfn[qes[i]],en[qes[i]]); i++; }
        i--;
        for(;lt<now;lt++){
          modify(dfn[s[poi[lt]]],-1);
          modify(dfn[f[lca[poi[lt]]][0]],1);
        }
      }
      sort(qes+1,qes+1+n,ruleb);
      sort(poi+1,poi+1+n,rule2);
      now=1;
      for(int i=1;i<=n;i++){
        int tmp=tme[qes[i]]-dep[qes[i]];
        while(now<=m&&t2[poi[now]]<tmp) now++;
        int lt=now;
        for(;now<=m&&t2[poi[now]]==tmp;now++){
          modify(dfn[t[poi[now]]],1);
          modify(dfn[lca[poi[now]]],-1);
        }
        while(i<=n&&tme[qes[i]]-dep[qes[i]]==tmp){ ans[qes[i]]+=query(dfn[qes[i]],en[qes[i]]); i++; }
        i--;
        for(;lt<now;lt++){
          modify(dfn[t[poi[lt]]],-1);
          modify(dfn[lca[poi[lt]]],1);
        }
      }
      for(int i=1;i<=n;i++) printf("%d ",ans[i]); putchar('
    ');
      return 0;
    }
    

    (O(n))

    有时间再补吧.

  • 相关阅读:
    点分治 / 点分树题目集
    HNOI2019 游记
    WC2019 题目集
    SA / SAM 题目集
    Min_25 筛小结
    NOIP2018 差点退役记
    Atcoder 乱做
    DP及其优化
    计数与概率期望小结
    分库分表之后全局id咋生成?
  • 原文地址:https://www.cnblogs.com/BruceW/p/11908901.html
Copyright © 2020-2023  润新知