CF1276F Asterisk Substrings [后缀自动机]

抄一下 \(\color{\black}{n}\color{\red}{antf}\) 的题解

我 nantf 怎么这么强啊…这题我不到半个小时就写掉了…为什么div1的时候只有一个人做掉这个 F 啊…这个不是我 nantf 随随便便就写掉的么…

这个 CF 评分 3300 的题为什么这么水啊,直接1A了(

我们先考虑不带 star 的,人人会做,\(\sum len_i - len_{fa_i}\)

考虑 star 在最前面和最后面,最后一个字符插入到 SAM 之前统计一下 \(\sum len_i - len_{fa_i}\)

然后考虑一下空字符和 star 字符,略。

考虑其他部分的答案,应该是以 \(i\) 为后缀的数量 \(\times\) \(i+2\) 为前缀的数量,这个挺好做的,下面讲一下。

我们在 \(i\) 对应的 \(sam\) 节点上插入 \(i+2\) 对应 \(rsam\) 的节点。

然后我们知道 \(fa_i\) 所对应的字符串是 \(i\) 的后缀,然后在 \(sam\) 上启发式合并,\(rsam\) 上面求个链并计算贡献,这题就没了。

对于我 nantf 来说代码不是特别好写么…所以丢这里好了


int n;
const int maxn = 4e5 + 54;
char s[maxn];
vector<int> g[maxn];
set<int> Real[maxn];
int Realsize[maxn], rt[maxn], rev[maxn];

int val[maxn], lg[maxn], dep[maxn];

int getmin(int x, int y) { return dep[x] < dep[y] ? x : y; }

int getpw(int x) { return 1 << x; }

struct mt {
  vector<int> g[maxn];
  int st[maxn][22], idx = 0;
  int dfn[maxn], rev[maxn];
  void dfs(int u) {
    st[dfn[u] = ++idx][0] = u;
    rev[dfn[u]] = u;
    for (int v : g[u]) {
      dfs(v);
      st[++idx][0] = u;
    }
  }

  int getlca(int x, int y) {
    if (dfn[x] > dfn[y]) x ^= y ^= x ^= y;
    x = dfn[x], y = dfn[y];
    int t = lg[y - x + 1];
    return getmin(st[x][t], st[y - getpw(t) + 1][t]);
  }
} qwq;

int getdis(int x, int y) {
  const int lca = qwq.getlca(x, y);
  return dep[x] + dep[y] - 2 * dep[lca];
}

int ans = 0;

void insert(int v, int u) {
  for (int t : Real[v]) {
    if (Real[u].count(t)) {
      continue;
    } else {
      auto it = Real[u].insert(t).fir;
      auto l = it;
      auto r = it;
      if (l == Real[u].begin())
        l = --Real[u].end();
      else
        --l;
      ++r;
      if (r == Real[u].end()) r = Real[u].begin();
      int a = qwq.rev[*l];
      int b = qwq.rev[*r];
      Realsize[u] += getdis(a, qwq.rev[t]) + getdis(b, qwq.rev[t]) - getdis(a, b);
    }
  }
  Real[v].clear();
  Realsize[v] = 0;
}

void dfs(int u) {
  for (int v : g[u]) {
    dfs(v);
    if (sz(Real[rt[u]]) < sz(Real[rt[v]])) swap(rt[u], rt[v]);
    insert(rt[v], rt[u]);
  }
  ans += (Realsize[rt[u]] >> 1) * val[u];
}

struct suffixautomaton {
  int ch[maxn][26];
  int cnt, las;

  suffixautomaton() { cnt = las = 1; }

  int len[maxn], fa[maxn];
  void ins(int c) {
    int p = las, np = las = ++cnt;
    len[np] = len[p] + 1;
    for (; p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
    if (!p) {
      fa[np] = 1;
    } else {
      int q = ch[p][c];
      if (len[q] == len[p] + 1) {
        fa[np] = q;
      } else {
        int nq = ++cnt;
        len[nq] = len[p] + 1;
        memcpy(ch[nq], ch[q], sizeof(ch[q]));
        fa[nq] = fa[q];
        fa[q] = fa[np] = nq;
        for (; p && ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
      }
    }
  }

  void build1() {
    rep(i, 2, cnt) g[fa[i]].pb(i);
    rep(i, 1, cnt) rt[i] = i;
    rep(i, 2, cnt) val[i] = len[i] - len[fa[i]];
    int p = 1;
    rep(i, 1, n - 2) {
      int c = s[i] - 'a';
      p = ch[p][c];
      Real[p].insert(qwq.dfn[rev[i + 2]]);
      Real[p].insert(qwq.dfn[1]);
      Realsize[p] = dep[rev[i + 2]] * 2;
    }
    dfs(1);
  }

  void build2() {
    rep(i, 2, cnt) { dep[i] = len[i], qwq.g[fa[i]].pb(i); }
    qwq.dfs(1);
    lg[1] = 0;
    rep(i, 2, maxn - 1) { lg[i] = lg[i >> 1] + 1; }
    rep(j, 1, 20) {
      rep(i, 1, qwq.idx - getpw(j) + 1) qwq.st[i][j] =
          getmin(qwq.st[i][j - 1], qwq.st[i + getpw(j - 1)][j - 1]);
    }
  }

  int getcnt() {
    int ans = 0;
    rep(i, 2, cnt) ans += len[i] - len[fa[i]];
    return ans;
  }
} sam, rsam;

signed main() {
  // code begin.
  in >> (s + 1), n = strlen(s + 1);
  rep(i, 1, n) {
    if (i == n) ans += sam.getcnt();
    sam.ins(s[i] - 'a');
  }
  Rep(i, n, 1) {
    if (i == 1) ans += rsam.getcnt();
    rsam.ins(s[i] - 'a');
    rev[i] = rsam.las;
  }
  ans += sam.getcnt();
  rsam.build2();
  sam.build1();
  ++ans, ++ans;
  out << ans << '\n';
  return 0;
  // code end.
}
posted @ 2020-04-07 17:42  _Isaunoya  阅读(246)  评论(0编辑  收藏  举报