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;
    }
    
  • 相关阅读:
    vue项目中,设置所有的input框不能输入空格
    自定义vue-baidu-map 组件的信息窗体infowindow(百度地图信息窗体)
    协程
    线程池--进程池--回调函数
    线程基本内容
    进程基本内容
    锁--互斥锁
    socketserver模块实现并发
    tcp的粘包现象
    一些内置的魔术方法
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/11484142.html
Copyright © 2011-2022 走看看