[BZOJ4719][P1600][NOIP2016]天天爱跑步[LCA+dfs序+差分]

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

\(s\)\(t\) 可以分成1个人从\(s\)到根, -1个人从根到 \(\text{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;
}
posted @ 2018-12-28 16:01  QvvQ  阅读(150)  评论(0编辑  收藏  举报