BZOJ3757: 苹果树【树上莫队】

Description

? 神犇家门口种了一棵苹果树。苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条。由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色。我们用一个BZOJ3757: 苹果树【树上莫队】到n之间的正整数来表示一种颜色。树上一共有n个苹果。每个苹果都被编了号码,号码为一个1到n之间的正整数。我们用0代表树根。只会有一个苹果直接根。

有许许多多的人来神犇家里膜拜神犇。可神犇可不是随便就能膜拜的。前来膜拜神犇的人需要正确回答一个问题,才能进屋膜拜神犇。这个问题就是,从树上编号为u的苹果出发,由树枝走到编号为v的苹果,路径上经过的苹果一共有多少种不同的颜色(包括苹果u和苹果v的颜色)?不过神犇注意到,有些来膜拜的人患有色盲症。具体地说,一个人可能会认为颜色a就是颜色b,那么他们在数苹果的颜色时,如果既出现了颜色a的苹果,又出现了颜色b的苹果,这个人只会算入颜色b,而不会把颜色a算进来。

神犇是一个好人,他不会强人所难,也就会接受由于色盲症导致的答案错误(当然答案在色盲环境下也必须是正确的)。不过这样神犇也就要更改他原先数颜色的程序了。虽然这对于神犇来说是小菜一碟,但是他想考验一下你。你能替神犇完成这项任务吗?

Input

输入第一行为两个整数n和m,分别代表树上苹果的个数和前来膜拜的人数。

接下来的一行包含n个数,第i个数代表编号为i的苹果的颜色Coli。

接下来有n行,每行包含两个数x和y,代表有一根树枝连接了苹果x和y(或者根和一个苹果)。

接下来有m行,每行包含四个整数u、v、a和b,代表这个人要数苹果u到苹果v的颜色种数,同时这个人认为颜色a就是颜色b。如果a=b=0,则代表这个人没有患色盲症。

Output

输出一共m行,每行仅包含一个整数,代表这个人应该数出的颜色种数。

Sample Input

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

Sample Output

2
1
2

HINT

0<=x,y,a,b<=N

N<=50000

1<=U,V,Coli<=N

M<=100000


思路

树上莫队板子

直接网上搜树上莫队就可以了


#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int n, m, cur = 0;
int top = 0, ind = 0, blosiz, blonum, root;
int res[N], p[N];
int fa[N][20], dep[N];
int c[N], st[N], dfn[N], bel[N];
bool vis[N];

struct Edge {
  int v, nxt;
} E[N];

int head[N], cnt = 0;

struct Query {
  int u, v, a, b, id;
} q[N];

bool operator < (const Query &a, const Query &b) {
  return bel[a.u] == bel[b.u] ? dfn[a.v] < dfn[b.v] : bel[a.u] < bel[b.u];
}

void addedge(int u, int v) {
  E[++cnt] = (Edge) {v, head[u]};
  head[u] = cnt;
}

int dfs(int u) {
  int siz = 0;
  dfn[u] = ++ind;
  for (int i = 1; i <= 18; i++)
    fa[u][i] = fa[fa[u][i - 1]][i - 1];
  for (int i = head[u]; i; i = E[i].nxt) {
    int v = E[i].v;
    if (v == fa[u][0]) continue;
    dep[v] = dep[u] + 1;
    fa[v][0] = u;
    siz += dfs(v);
    if (siz >= blosiz) {
      ++blonum;
      for (int k = 1; k <= siz; k++) {
        bel[st[top--]] = blonum;
      }
      siz = 0;
    }
  }
  st[++top] = u;
  return siz + 1;
}

int lca(int x, int y) {
  if (dep[x] < dep[y]) swap(x, y);
  int delta = dep[x] - dep[y];
  for (int i = 0; i <= 18; i++) {
    if ((delta >> i) & 1) {
      x = fa[x][i];
    }
  }
  if (x == y) return x;
  for (int i = 18; i >= 0; i--) {
    if (fa[x][i] != fa[y][i]) {
      x = fa[x][i];
      y = fa[y][i];
    }
  }
  return fa[x][0];
}

void reverse(int x) {
  if (!vis[x]) {
    vis[x] = 1;
    if (++p[c[x]] == 1) ++cur;
  } else {
    vis[x] = 0;
    if (--p[c[x]] == 0) --cur;
  }
}

void solve(int u, int v) {
  while (u != v) {
    if (dep[u] > dep[v]) {
      reverse(u);
      u = fa[u][0];
    } else {
      reverse(v);
      v = fa[v][0];
    }
  }
}

int main() {
#ifdef dream_maker
  freopen("input.txt", "r", stdin);
#endif
  scanf("%d %d", &n, &m);
  blosiz = sqrt(n << 1);
  for (int i = 1; i <= n; i++) scanf("%d", &c[i]);
  for (int i = 1; i <= n; i++) {
    int u, v;
    scanf("%d %d", &u, &v);
    if (!u) root = v;
    if (!v) root = u;
    if (u && v) {
      addedge(u, v);
      addedge(v, u);
    }
  }
  dfs(root);
  if (top) {
    blonum++;
    while (top) {
      bel[st[top--]] = blonum;
    }
  }
  for (int i = 1; i <= m; i++) {
    scanf("%d %d %d %d", &q[i].u, &q[i].v, &q[i].a, &q[i].b);
    if (dfn[q[i].u] > dfn[q[i].v]) swap(q[i].u, q[i].v);
    q[i].id = i;
  }
  sort(q + 1, q + m + 1);
  int t = lca(q[1].u, q[1].v);
  solve(q[1].u, q[1].v);
  reverse(t);
  res[q[1].id] = cur - (p[q[1].a] && p[q[1].b] && q[1].a != q[1].b);
  reverse(t);
  for (int i = 2; i <= m; i++) {
    solve(q[i - 1].u, q[i].u);
    solve(q[i - 1].v, q[i].v);
    t = lca(q[i].u, q[i].v);
    reverse(t);
    res[q[i].id] = cur - (p[q[i].a] && p[q[i].b] && q[i].a != q[i].b);
    reverse(t);
  }
  for (int i = 1; i <= m; i++)
    printf("%d\n", res[i]);
  return 0;
}     

BZOJ3757: 苹果树【树上莫队】

上一篇:BST_traverse(中序遍历,前序遍历,后序遍历)


下一篇:Android VideoView播放网络视频简介(转)