zoukankan      html  css  js  c++  java
  • 十二省联考2019 字符串问题

    原题链接

    不会\(\text{SAM}\)的蒟蒻只好用\(\text{SA}\)来水了。

    先读懂题意(窝在读题意这儿花了快\(20\)min).

    简单来说,是给你一个字符串,然后给你两个区间的集合\(A,B\),一共有\(m\)\(A_i\)\(B_j\)的支配关系。若\(A_i,B_j\)有支配关系,则从\(A_i\)\(B_j\)连边;若\(B_j\)\(A_i\)的前缀,则珂以从\(B_j\)连向\(A_i\)。每个\(A_i\)有权值,是\(A_i\)区间的长度。当你把上述的图建好后,她要你求图中的最长路。如果珂以无限长,输出-1

    一看就懵逼的神仙字符串图论题

    来肝这题的神仙们应该都知道最长路咋求吧?就是先拓扑排序一下,然后从后往前\(\text{dp}\)。这里就不讲了。

    分析一下,这张图上一共的点数是不超过\(|A|+|B|\),即是线性的。但边数珂能高达\(m+|A|\cdot|B|\)。再仔细地分析一下,从\(A_i\)\(B_j\)连的边数是\(m\),是线性的。问题就转化成了优化\(B_j\)\(A_i\)连边的过程。

    结合\(B_j\)\(A_i\)的前缀,我们很容易想到用\(\text{SA}\)。我们找到\(B_j\)\(\text{sa}\)上的位置,设为\(\text{pos}\)\(B_j\)连向的所有\(A_i\)必须满足\(\text{lcp}\)\(\ge|B_j|\)的,在\(\text{sa}\)上必须是一个区间,且包含\(\text{pos}\)。设这个区间为\([l,r]\),那么必须满足:\(\min\limits_{l+1\le i\le r}height_i\ge |B_j|\),且\(l\)尽量忘左,\(r\)尽量往右。找\(l,r\)的过程珂以通过在\(\text{height}\)数组上建出\(\text{ST}\)表之后二分求出。然后只要再用线段树优化建图就珂以了。

    但这样有个问题:你不能保证在\([l,r]\)区间内的\(|A_i|\ge |B_j|\)。这样只能得到\(80\)分。

    所以我们需要考虑更好的做法:用可持久化线段树优化建图。按照\(|A_i|\)从大到小建树,每个\(A_i\)只在第\(n-|A_i|\)棵可持久化线段树建树时加入。在可持久化线段树上,第\(i\)棵树上的点要向第\(i-1\)棵树上的对应点连边(除非没有),这样珂以保证每一次加入的\(B_j\)的长度\(\le |A_i|\)。具体细节就康代码吧。

    总时间复杂度:\(O(N\log N)\),空间复杂度:\(O(N\log N)\)

    代码:

    // Code by H~$~C
    #include <bits/stdc++.h>
    using namespace std;
    
    #ifndef LOCAL_JUDGE
    static char _in_buf[100000], *_in_p1 = _in_buf, *_in_p2 = _in_buf;
    #define gc (__builtin_expect(_in_p1 == _in_p2, 0) && (_in_p2 = (_in_p1 = _in_buf) + \
            fread(_in_buf, 1, 100000, stdin), _in_p1 == _in_p2) ? -1 : *_in_p1++)
    #else
    #define gc getchar()
    #endif
    inline int read() {
      register char ch = gc;
      register int x = 0;
      while (ch < 48 || ch > 57) ch = gc;
      while (ch > 47 && ch < 58) x = (x << 3) + (x << 1) + (ch ^ 48), ch = gc;
      return x;
    }
    
    static const int Maxn = 200005;
    static const int Maxs = 5000005;
    
    int n, na, nb, N, m;
    char str[Maxn];
    int la[Maxn], ra[Maxn], lb[Maxn], rb[Maxn];
    vector<int> A[Maxn];
    int ST[Maxn][20];
    
    int wa[Maxn], wb[Maxn], wc[Maxn], _s[Maxn];
    int sa[Maxn], rnk[Maxn], height[Maxn];
    template<typename T>
    void build_SA(T *ss, int n, int m) {
      register int *x = wa, *y = wb, *tmp;
      register int i, j, w;
      for (i = 1; i <= n; ++i) _s[i] = ss[i];
      for (i = 1; i <= n; ++i) x[i] = _s[i], y[i] = i;
      for (i = 1; i <= m; ++i) wc[i] = 0;
      for (i = 1; i <= n; ++i) wc[x[i]]++;
      for (i = 2; i <= m; ++i) wc[i] += wc[i - 1];
      for (i = n; i >= 1; --i) sa[wc[x[y[i]]]--] = y[i];
      for (w = 1; w <= n; w <<= 1) {
        register int tot = 0;
        for (i = n - w + 1; i <= n; ++i) y[++tot] = i;
        for (i = 1; i <= n; ++i) if (sa[i] > w) y[++tot] = sa[i] - w;
        for (i = 1; i <= m; ++i) wc[i] = 0;
        for (i = 1; i <= n; ++i) wc[x[i]]++;
        for (i = 2; i <= m; ++i) wc[i] += wc[i - 1];
        for (i = n; i >= 1; --i) sa[wc[x[y[i]]]--] = y[i];
        tmp = x, x = y, y = tmp, x[sa[1]] = 1, tot = 1;
        for (i = 2; i <= n; ++i)
          x[sa[i]] = ((y[sa[i]] == y[sa[i - 1]] && y[sa[i] + w] == y[sa[i - 1] + w]) ? tot : ++tot);
        if (tot == n) break; m = tot;
      }
      for (i = 1; i <= n; ++i) rnk[sa[i]] = i;
      for (i = 1, w = 0; i <= n; ++i) {
        if (rnk[i] == 1) continue;
        if (w) --w;
        int j = sa[rnk[i] - 1];
        while (i + w <= n && j + w <= n && _s[i + w] == _s[j + w]) w++;
        height[rnk[i]] = w;
      }
    }
    // sa[rank] = name, rnk[name] = rank
    // height[i] = lcp(suffix(sa[i - 1]), suffix(sa[i]))
    
    vector<int> g[Maxs];
    int deg[Maxs], val[Maxs];
    inline void clear_all(int u) { g[u].clear(), deg[u] = val[u] = 0; }
    inline void add_edge(int u, int v) { g[u].push_back(v), deg[v]++; }
    
    struct Node {
      int id;
      Node *l, *r;
      Node() { }
      Node(int id, Node *l, Node *r)
      : id(id), l(l), r(r) { }
    } *root[Maxn], pool[Maxs], *cur_pointer = pool;
    inline Node *newnode(int id, Node *l = NULL, Node *r = NULL) {
      return &(*++cur_pointer = Node(id, l, r));
    }
    int insert(Node *&p, int l, int r, int pos) {
      if (!p) {
        p = newnode(++N);
        clear_all(N);
      }
      else {
        p = newnode(p->id, p->l, p->r);
        clear_all(++N);
        add_edge(N, p->id);
        p->id = N;
      }
      if (l == r) return p->id;
      int mid = (l + r) >> 1, res;
      if (pos <= mid) res = insert(p->l, l, mid, pos);
      else res = insert(p->r, mid + 1, r, pos);
      if (p->l) add_edge(p->id, p->l->id);
      if (p->r) add_edge(p->id, p->r->id);
      return res;
    }
    void link_edge(Node *p, int l, int r, int L, int R, int u) {
      if (!p) return ;
      if (L == l && r == R) return add_edge(u, p->id);
      int mid = (l + r) >> 1;
      if (R <= mid) return link_edge(p->l, l, mid, L, R, u);
      if (L > mid) return link_edge(p->r, mid + 1, r, L, R, u);
      link_edge(p->l, l, mid, L, mid, u);
      link_edge(p->r, mid + 1, r, mid + 1, R, u);
    }
    
    int que[Maxs], qh, qe;
    long long dp[Maxs];
    
    void solve() {
      cur_pointer = pool;
      register int i, j;
      register char ch;
      while ((ch = gc) < 33);
      for (n = 0; ch > 32; ch = gc) str[++n] = ch;
      for (na = read(), i = 1; i <= na; ++i)
        la[i] = read(), ra[i] = read();
      for (nb = read(), i = 1; i <= nb; ++i)
        lb[i] = read(), rb[i] = read();
      N = na + nb;
      build_SA<char>(str, n, 128);
      for (i = 1; i <= n; ++i) ST[i][0] = height[i];
      for (j = 1; j < 20; ++j)
        for (i = 1; i + (1 << j) - 1 <= n; ++i)
          ST[i][j] = min(ST[i][j - 1], ST[i + (1 << (j - 1))][j - 1]);
      
      for (i = 1; i <= N; ++i) clear_all(i);
      for (m = read(), i = 1; i <= m; ++i) {
        int x = read(), y = read();
        add_edge(x, y + na);
      }
      for (i = 1; i <= n; ++i) A[i].clear();
      for (i = 1; i <= na; ++i) {
        val[i] = ra[i] - la[i] + 1;
        A[val[i]].push_back(i);
      }
      
      root[n + 1] = NULL;
      for (i = n; i >= 1; --i) {
        root[i] = root[i + 1];
        for (int &x: A[i]) {
          int id = insert(root[i], 1, n, rnk[la[x]]);
          add_edge(id, x);
        }
      }
      for (i = 1; i <= nb; ++i) {
        int l = rnk[lb[i]], r = rnk[lb[i]];
        int len = rb[i] - lb[i] + 1;
        /* get the leftest position */ {
          for (j = 0; l - (1 << j) + 1 >= 2; ++j);
          for (; ~--j; ) {
            if (l - (1 << j) + 1 >= 2 && ST[l - (1 << j) + 1][j] >= len) {
              l -= (1 << j);
            }
          }
        }
        /* get the rightest position */ {
          for (j = 0; r + (1 << j) <= n; ++j);
          for (; ~--j; ) {
            if (r + (1 << j) <= n && ST[r + 1][j] >= len) {
              r += (1 << j);
            }
          }
        }
        link_edge(root[len], 1, n, l, r, i + na);
      }
      
      qh = qe = 0;
      for (i = 1; i <= N; ++i) {
        if (!deg[i]) que[qe++] = i;
      }
      while (qh < qe) {
        int u = que[qh++];
        for (int &v: g[u]) {
          if (!--deg[v]) {
            que[qe++] = v;
          }
        }
      }
      if (qe != N) {
        puts("-1");
        return ;
      }
      
      for (i = N - 1; ~i; --i) {
        int u = que[i];
        dp[u] = 0;
        for (int &v: g[u]) {
          dp[u] = max(dp[u], dp[v]);
        }
        dp[u] += val[u];
      }
      printf("%lld\n", *max_element(dp + 1, dp + N + 1));
    }
    
    int main() {
      int tests = read();
      while (tests--) solve();
      return 0;
    }
    

    跑得炒鸡慢\(\ldots\)最大的点用了\(7.40\)

  • 相关阅读:
    5G名词术语
    什么是IPv6
    如何用SecureCRT 登录eNSP模拟器里的设备
    numpy-排序
    numpy-tile 数组复制
    经纬度计算距离与方位角
    numpy-添加操作大全
    高效编程之 concurrent.future
    linux命令 集合
    PostgreSQL-表空间
  • 原文地址:https://www.cnblogs.com/libra9z/p/12388830.html
Copyright © 2011-2022 走看看