zoukankan      html  css  js  c++  java
  • BZOJ3757: 苹果树【树上莫队】

    Description

    ​ 神犇家门口种了一棵苹果树。苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条。由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色。我们用一个![img](file:///C:/DOCUME1/ADMINI1/LOCALS~1/Temp/msohtml1/01/clip_image002.gif)到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
    ", res[i]);
      return 0;
    }     
    
  • 相关阅读:
    chrome/edge 自签名证书造成浏览器无法访问
    linux 下 取进程占用内存(MEM)最高的前10个进程
    总结记录一下我对YZ数据中台指标相关平台的理解感悟与思考
    史上最全之微信群发拼手气红包测试用例
    vue使用filterBy,orderBy实现搜索筛选功能
    前端处理防抖和节流
    箭头函数()=>{}与function的区别
    html不用任何控件上传文件
    Java必备知识--线程池
    Java必备知识--日志框架
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/10194868.html
Copyright © 2011-2022 走看看