zoukankan      html  css  js  c++  java
  • [BZOJ4719][P1600][NOIP2016]天天爱跑步[LCA+dfs序+差分]

    题意:一棵树,有 (m) 个人从 (s_i)(t_i) 跑步,每个人的速度都是1,每个点有一个观察员 当一个人在w[i]经过第 (i) 个点的时候 第 (i) 个点的答案+1 问每个点的答案

    (s)(t) 可以分成1个人从(s)到根, -1个人从根到 ( ext{fa[lca(s,t)]})(向上的部分)和1个人从根到 (t),-1个人从lca走到根(向下的部分)

    (s) 向上的部分中 若(w[x]+dep[x]=dep[s])
    则对x点有贡献

    向下的部分同理,只是式子有点差别

    然后求出dfs序,对于每个点x统计贡献,统计贡献就相当于查一个数在一个区间中的出现次数,这个又可以差分成前缀出现次数相减,((a[r] - a[l-1]) 的形式),需要推一下..

    对x有贡献的路径一定有一个端点在x子树内,同时dfs序上x的子树一定是一段连续的区间 那么就可以把上述 (a[r]) 中的 (r) 设为dfs完x子树的位置,(a[l-1])(l-1) 设为在dfs到x子树之前的位置

    然后是类似扫描线的思想,cnt数组表示扫到当前这个点时各种值出现的次数

    有负数需要加偏移量,学习了一种不需要加的新姿势,观察了一下O2并没有跳过这个,但-Wall会提示,可能有些危险

    数组嵌套爽歪歪

    #include <bits/stdc++.h>  
    using namespace std;
    const int MAXN = 3e5+7;
    #define pb push_back
    #define mp make_pair
    #define xx first
    #define yy second
    typedef pair<int,bool> pii;
    #define lop(i,a,b) for(int i = (a), i##end = (b); i < i##end; ++i) 
    int Cnt[MAXN*3], lca[MAXN], siz[MAXN], top[MAXN], idx, tot, head[MAXN], pos[MAXN], ans[MAXN], s[MAXN], t[MAXN], w[MAXN], n, m, dep[MAXN], anc[MAXN], son[MAXN], *cnt = Cnt + 450000;
    vector<pii>q[MAXN], v[MAXN]; 
    struct Edge {int v, next;}G[MAXN*2];
      
    void add(int u, int v) {
      G[++tot] = (Edge) {v, head[u]}; head[u] = tot;
    }
    #define cur G[i].v
    void dfs1(int u) {
      siz[u] = 1;
      for(int i = head[u]; i; i = G[i].next) {
        if (cur == anc[u]) continue;
        anc[cur] = u, dep[cur] = dep[u] + 1;
        dfs1(cur); siz[u] += siz[cur];
        if (siz[cur] >= siz[son[u]]) son[u] = cur;
      }
    }
      
    void dfs2(int u, int t) {
      top[u] = t; pos[u] = ++idx;
      q[ pos[u]-1 ].pb(mp(u, 0));
      if (son[u]) dfs2(son[u], t);
      for(int i = head[u]; i; i = G[i].next) 
        if (cur != anc[u] && cur != son[u]) dfs2(cur, cur); 
      q[idx].pb(mp(u, 1));
    }
    #undef cur
      
    inline int LCA(int u, int v) {
      while(top[u] != top[v]) 
        dep[top[u]] >= dep[top[v]] ? u = anc[top[u]] : v = anc[top[v]];
      return dep[u] < dep[v] ? u : v;
    }
      
    int main() {
      scanf("%d%d", &n, &m);
      lop(i,1,n) {
        int u, v;
        scanf("%d%d", &u, &v);
        add(u, v), add(v, u);
      }
      dfs1(1), dfs2(1, 1);
      lop(i,1,n+1) scanf("%d", w+i);
      lop(i,1,m+1) scanf("%d%d", s+i, t+i), lca[i] = LCA(s[i], t[i]), v[pos[s[i]]].pb(mp(dep[s[i]], 1)), v[pos[anc[lca[i]]]].pb(mp(dep[s[i]], 0));
      lop(x,1,n+1) {
        lop(i,0,v[x].size())
          cnt[v[x][i].xx] += v[x][i].yy ? 1 : -1;
        lop(i,0,q[x].size())
          ans[q[x][i].xx] += (q[x][i].yy ? 1 : -1) * cnt[dep[q[x][i].xx] + w[q[x][i].xx]];
        vector<pii>().swap(v[x]);
      }
      memset(Cnt, 0, sizeof Cnt);
      lop(i,1,m+1) v[pos[t[i]]].pb(mp(-dep[s[i]] + 2*dep[lca[i]], 1)), v[pos[lca[i]]].pb(mp(-dep[s[i]] + 2*dep[lca[i]], 0));
      lop(x,1,n+1) {
        lop(i,0,v[x].size())
          cnt[v[x][i].xx] += v[x][i].yy ? 1 : -1;
        lop(i,0,q[x].size())
          ans[q[x][i].xx] += (q[x][i].yy ? 1 : -1) * cnt[dep[q[x][i].xx] - w[q[x][i].xx]];
      }
      lop(i,1,n+1) printf("%d ", ans[i]);
      return 0;
    }
    
  • 相关阅读:
    删除当前目录下除了system目录的其他文件
    单例设计模式
    系统工程师
    字符串翻转
    教育
    得到b相对于a的路径
    一段处理事务的代码
    搭讪
    win 8 ,vs2011 编程环境下,动软生成器无法连接上 sql server 2008 r2
    从asp网站编程转行到asp.net网站编程的过程
  • 原文地址:https://www.cnblogs.com/storz/p/10191128.html
Copyright © 2011-2022 走看看