zoukankan      html  css  js  c++  java
  • 【border树】【P2375】动物园

    Description

    给定一个字符串 (S),对每个前缀求长度不超过该前缀一半的公共前后缀个数。

    共有 (T) 组数据,每组数据的输出是 (O(1)) 的。

    Limitations

    (1 leq |S| leq 10^6,~1 leq T leq 5)

    Solution

    好水的NOI题

    建出 (border) 树,对于树上每个节点,它的所有 (border) 与它的所有祖先一一对应。这是因为每个节点的父亲是它的最长 (border),数学归纳可以证明这个结论。

    考虑对于这棵 (border) 树上任何一个节点到根的路径上节点的编号一定是单调减小的,这是因为每个结点的最长 (border) 显然小于自身。

    因此,对于一个点 (u),如果 (v) 是一个合法的 (border),那么 (v) 的祖先一定是合法的 (border)。所以合法的 (border) 一定是一条以根为一个端点的链。

    综合上面两条可以得出,如果 (u) 的合法 (border) 对应链的另一个端点为 (v),那么 (u) 的孩子的合法 (border) 的对应端点一定在 (v)(u) 的路径上,因此这个端点位置是单调变深的。

    所以只需要在 dfs 的时候维护根节点到该节点的链,并维护当前节点的答案位置即可。由于答案位置只会变大,因此答案位置的寻找是均摊 (O(1)) 的,暴力往下循环即可。

    总时间复杂度 (O(T |S|))

    Code

    #include <cstdio>
    #include <cstring>
    #include <vector>
    
    const int maxn = 1000006;
    const int MOD = 1000000007;
    
    int T;
    int n, top;
    ll ans;
    char MU[maxn];
    int border[maxn], pos[maxn], stack[maxn];
    std::vector<int>son[maxn];
    
    void KMP();
    void clear();
    int ReadStr(char *p);
    void dfs(const int u);
    
    int main() {
      freopen("1.in", "r", stdin);
      qr(T);
      while (T--) {
        clear();
        n = ReadStr(MU + 1);
        KMP();
        for (int i = 1; i <= n; ++i) {
          son[border[i]].push_back(i);
        }
        dfs(0);
        qw(ans, '
    ', true);
      }
      return 0;
    }
    
    int ReadStr(char *p) {
      auto beg = p;
      do *p = IPT::GetChar(); while ((*p > 'z') || (*p < 'a'));
      do *(++p) = IPT::GetChar(); while ((*p >= 'a') && (*p <= 'z'));
      *p = 0;
      return p - beg;
    }
    
    void KMP() {
      for (int i = 2, j = 0; i <= n; ++i) {
        while (j && (MU[i] != MU[j + 1])) j = border[j];
        if (MU[i] == MU[j + 1]) ++j;
        border[i] = j;
      }
    }
    
    void clear() {
      ans = 1; top = -1;
      for (auto &u : son) {
        u.clear();
      }
      memset(pos, 0, sizeof pos);
    }
    
    void dfs(const int u) {
      stack[++top] = u;
      for (auto v : son[u]) {
        int dv = v >> 1;
        int &_ans = pos[v];
        _ans = pos[u];
        while ((_ans < top) && (stack[_ans + 1] <= dv)) ++_ans;
        dfs(v);
      }
      if (pos[u]) {
        (ans += pos[u] * ans) %= MOD;
      }
      --top;
    }
    
  • 相关阅读:
    (转)Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks(更快的RCNN:通过区域提议网络实现实时)
    vi常用命令
    Windows与Ubuntu通过ssh传文件
    Windows建立FTP服务器与Ubuntu互传文件
    备忘
    一些大牛博客地址
    springboot、springcloud学习记录
    我的git记录
    svn
    我的linux命令记录
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/11484142.html
Copyright © 2011-2022 走看看