zoukankan      html  css  js  c++  java
  • 【IOI 2018】Werewolf 狼人

    虽然作为IOI的Day1T3,但其实不是一道很难的题,或者说这道题其实比较套路吧。

    接下来讲解一下这个题的做法:

     如果你做过NOI 2018的Day1T1,并且看懂了题面,那你很快就会联想到这道题,因为两者都是问某一个点能到达的点集,只不过限制在点上还是边上的问题。

    $Kruskal$重构树可以把在边上的限制转化成点上的,于是能解决NOI 2018的Day1T1;那么这道题就可以直接做,因为限制已经在点上了。

    具体来讲,从$s$点只能走编号$>=l$的点,那么我们就构建一棵树,使得任意一个非根节点的标号都比它父亲都大。要做到这一点,只要从大到小枚举点$x$,尝试把它加入树中,枚举所有与他相邻且编号比它大的节点$y$,如果$x$和$y$不在一个连通块里,就让$y$的并查集的根成为$x$的儿子,并在并查集里也连起来。

    我们要对$s$和$t$都做一遍,也就是从大到小建一棵树$Tu$,从小到大建一棵树$Tv$。那么从$s$出发只走$>=l$的点能走到的点在$Tu$上就是一个子树;对$t$同理。

    我们能用树上倍增找到$s$能走到的$Tu$上的那个子树,和$t$能走到的$Tv$的那个子树,这两者在两棵树上分别都对应$Dfs$序上的一段区间。

    我们需要找到一个能变身的地点,它要满足能从$s$和$t$都能走到它,也就是在这两段$Dfs$上的区间中存在相同的元素。如何判断两个序列上的两个区间是否同时拥有某个元素,一般情况下我不会做,但是由于每个元素都恰好在一个序列中出现了一次,我们可以利用这个特殊的性质来解决。一个询问的答案是$1$当且仅当存在一个点,其中它在第一个序列中的位置在所求区间内,第二个序列也是。我们发现这就是一个简单的二维数点,我们把所有点在第一个和第二个$Dfs$序中中出现的位置分别当成$(x,y)$,一个询问就是判定一个二维平面上的一个矩形内是否有点。用离线树状数组实现比较方便。

    $igodot$技巧&套路:

    •  点上限制和$Kruskal$重构树在边上的限制的联系
    • 两个$Dfs$序区间是否有相同元素向数点问题的转化

    由于这道题本质是一道传统题,所以我用了传统的实现方法:

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    const int LOG = 21;
    const int N = 200005;
    
    int n, m, nq, qi;
    int ans[N];
    vector<int> g[N];
    
    struct Que {
      int x, y, id, ty, f;
      friend bool operator < (const Que &a, const Que &b) {
        return (a.x != b.x)? (a.x < b.x) : (a.ty < b.ty);
      }
    } q[N * 7];
    
    struct Dsu {
      int fa[N];
      void Init(int n) {
        for (int i = 1; i <= n; ++i) fa[i] = i;
      }
      int Sk(int x) {
        return fa[x] == x? x : fa[x] = Sk(fa[x]);
      }
    } U, V;
    
    struct Tree {
      vector<int> g[N];
      int tot, din[N], dfn[N], row[N], ed[N], dep[N], gr[LOG][N];
      void Add(int x, int y) {
        g[x].push_back(y), ++din[y];
      }
      void Dfs(int x) {
        dfn[x] = ++tot, row[tot] = x;
        for (int i = 1; i < LOG; ++i)
          if (gr[i - 1][x]) gr[i][x] = gr[i - 1][gr[i - 1][x]];
        for (int v : g[x]) {
          gr[0][v] = x, dep[v] = dep[x] + 1;
          Dfs(v);
        }
        ed[x] = tot;
      }
      void Build() {
        int rt = -1;
        for (int i = 1; i <= n; ++i) if (din[i] == 0) rt = i;
        if (rt == -1) throw;
        Dfs(rt);
      }
    } Tu, Tv;
    
    int Fly_u(int x, int lim) {
      for (int i = LOG - 1; ~i; --i)
        if (Tu.gr[i][x] && Tu.gr[i][x] >= lim) x = Tu.gr[i][x];
      return x;
    }
    int Fly_v(int x, int lim) {
      for (int i = LOG - 1; ~i; --i)
        if (Tv.gr[i][x] && Tv.gr[i][x] <= lim) x = Tv.gr[i][x];
      return x;
    }
    
    namespace BIT {
      int t[N];
      void Add(int x) {
        for (; x <= n; x += x & -x) ++t[x];
      }
      int Qr(int x, int r = 0) {
        for (; x; x -= x & -x) r += t[x];
        return r;
      }
    }
    
    
    int main() {
      scanf("%d%d%d", &n, &m, &nq);
      for (int i = 1, x, y; i <= m; ++i) {
        scanf("%d%d", &x, &y);
        g[++x].push_back(++y), g[y].push_back(x);
      }
      U.Init(n), V.Init(n);
      for (int i = n; i >= 1; --i) {
        for (int j : g[i]) {
          if (j < i) continue;
          int y = U.Sk(j);
          if (i != y) U.fa[y] = i, Tu.Add(i, y);
        }
      }
      for (int i = 1; i <= n; ++i) {
        for (int j : g[i]) {
          if (j > i) continue;
          int y = V.Sk(j);
          if (i != y) V.fa[y] = i, Tv.Add(i, y);
        }
      }
      Tu.Build(), Tv.Build();
      for (int i = 1; i <= n; ++i) {
        q[i].x = Tu.dfn[i], q[i].y = Tv.dfn[i];
        q[i].ty = 1;
      }
      qi = n;
      for (int i = 1, x, y, l, r; i <= nq; ++i) {
        scanf("%d%d%d%d", &x, &y, &l, &r);
        ++x, ++y, ++l, ++r;
        int u = Fly_u(x, l), v = Fly_v(y, r);
        q[++qi] = (Que){ Tu.ed[u], Tv.ed[v], i, 2, 1 };
        if (Tu.dfn[u] > 1) q[++qi] = (Que){ Tu.dfn[u] - 1, Tv.ed[v], i, 2, -1 };
        if (Tv.dfn[v] > 1) q[++qi] = (Que){ Tu.ed[u], Tv.dfn[v] - 1, i, 2, -1 };
        if (Tu.dfn[u] > 1 && Tv.dfn[v] > 1) q[++qi] = (Que){ Tu.dfn[u] - 1, Tv.dfn[v] - 1, i, 2, 1 };
      }
      sort(q + 1, q + 1 + qi);
      for (int i = 1; i <= qi; ++i) {
        if (q[i].ty == 1) {
          BIT::Add(q[i].y);
        } else {
          ans[q[i].id] += q[i].f * BIT::Qr(q[i].y);
        }
      }
      for (int i = 1; i <= nq; ++i) {
        if (ans[i] < 0) throw;
        printf("%d
    ", (bool)ans[i]);
      }
    
      return 0;
    }
    View Code
  • 相关阅读:
    无规矩不成方圆,聊一聊 Spring Boot 中 RESTful 接口设计规范
    一次SQL查询优化原理分析(900W+数据,从17s到300ms)
    重磅!GitHub官方开源新命令行工具
    JVM调优的反思与总结
    SpringMVC 进阶版
    《四大点,搞懂Redis到底快在哪里?》
    《Docker基础与实战,看这一篇就够了》
    带你从头到尾捋一遍MySQL索引结构
    MySQL信息提示不是英文问题
    完美解决windows+ngnix+phpcgi自动退出的问题
  • 原文地址:https://www.cnblogs.com/Dance-Of-Faith/p/9656125.html
Copyright © 2011-2022 走看看