BZOJ3757: 蘋果樹【樹上莫隊】

Description

​ 神犇家門口種了一棵蘋果樹。蘋果樹做爲一棵樹,固然是呈樹狀結構,每根樹枝鏈接兩個蘋果,每一個蘋果均可以沿着一條由樹枝構成的路徑連到樹根,並且這樣的路徑只存在一條。因爲這棵蘋果樹是神犇種的,因此蘋果都發生了變異,變成了各類各樣的顏色。咱們用一個img到n之間的正整數來表示一種顏色。樹上一共有n個蘋果。每一個蘋果都被編了號碼,號碼爲一個1到n之間的正整數。咱們用0表明樹根。只會有一個蘋果直接根。c++

有許許多多的人來神犇家裏膜拜神犇。可神犇可不是隨便就能膜拜的。前來膜拜神犇的人須要正確回答一個問題,才能進屋膜拜神犇。這個問題就是,從樹上編號爲u的蘋果出發,由樹枝走到編號爲v的蘋果,路徑上通過的蘋果一共有多少種不一樣的顏色(包括蘋果u和蘋果v的顏色)?不過神犇注意到,有些來膜拜的人患有色盲症。具體地說,一我的可能會認爲顏色a就是顏色b,那麼他們在數蘋果的顏色時,若是既出現了顏色a的蘋果,又出現了顏色b的蘋果,這我的只會算入顏色b,而不會把顏色a算進來。spa

神犇是一個好人,他不會強人所難,也就會接受因爲色盲症致使的答案錯誤(固然答案在色盲環境下也必須是正確的)。不過這樣神犇也就要更改他原先數顏色的程序了。雖然這對於神犇來講是小菜一碟,可是他想考驗一下你。你能替神犇完成這項任務嗎?code

Input

輸入第一行爲兩個整數n和m,分別表明樹上蘋果的個數和前來膜拜的人數。ip

接下來的一行包含n個數,第i個數表明編號爲i的蘋果的顏色Coli。input

接下來有n行,每行包含兩個數x和y,表明有一根樹枝鏈接了蘋果x和y(或者根和一個蘋果)。it

接下來有m行,每行包含四個整數u、v、a和b,表明這我的要數蘋果u到蘋果v的顏色種數,同時這我的認爲顏色a就是顏色b。若是a=b=0,則表明這我的沒有患色盲症。io

Output

輸出一共m行,每行僅包含一個整數,表明這我的應該數出的顏色種數。class

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
2im

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;
}
相關文章
相關標籤/搜索