「WC2016」论�战捆竹竿
「WC2016」论战捆竹竿

2020 年 04 月 26 日

给定一个字符串 ss,假设其 border 集合为 SS,则每次你可以在 ss 后面接上一个长度为 sx|s| - x 的字符串,其中 xSx \in S。问在总长度 w\leq w 的情况下有多少种可能的本质不同的长度。

n5×105, w1018n \leq 5 \times 10^5,\ w \leq 10^{18}

题解

Update:这个做法已经被 lyx 老师 Hack

border 的贡献是若干端等差数列,不妨设其中一段为 kx+bkx + b,其中 x[0,l]x \in [0,l],考虑其产生的贡献整理后可以理解为三种:

  • 长度为 bb 的贡献,可以选择 inf\inf 次;
  • 长度为 lk+blk + b 的贡献,可以选择 inf\inf 次;
  • 长度为 (0...l)k+b(0...l)k + b 的贡献,可以选择 11 次。

考虑前两种贡献,就是朴素的同余最短路问题。考虑先计算出他们的 dis 数组,再转移上第三类贡献。

对于每一种等差数列分开处理,分模 kk 的余数讨论,容易发现可以直接用单调队列维护下转移。

同余最短路跑 spfa 是线性的,可以直接用(。

时间复杂度 O(nlogw)O(n \log w)

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int T, n, fl, mod;
char s[N];
long long w, ans, t[N], dis[2][N];
vector<int> key;
struct atom {
  int b, k, l;
};
vector<atom> res;
int nod;
struct node {
  int pre, nxt, i;
  long long x;
} e[N << 1];
inline void init() { nod = 0; }
struct myset {
  int lim, hed, til;
  inline void reset(int l) { hed = til = 0, lim = l; }
  inline void pop_front() {
    if (hed == til) hed = til = 0;
    else {
      e[e[til].pre].nxt = 0;
      til = e[til].pre;
    }
  }
  inline void pop_back() {
    if (hed == til) hed = til = 0;
    else {
      e[e[hed].nxt].pre = 0;
      hed = e[hed].nxt;
    }
  }
  inline void push_back(int i, long long x) {
    e[++nod].i = i, e[nod].x = x;
    if (!hed) {
      hed = til = nod;
      e[nod].pre = e[nod].nxt = 0;
    } else {
      e[nod].nxt = hed, e[hed].pre = nod;
      hed = nod;
    }
  }
  inline void insert(int i, long long x) {
    while (hed && e[hed].x > x) pop_back();
    push_back(i, x);
    while (e[til].i < i - lim) pop_front();
  }
  inline long long query() { return e[til].x; }
};
myset st[N];
namespace h {
const int b = 131131, p1 = 998244353, p2 = 1e9 + 7;
int f[N], g[N], pf[N], pg[N];
vector<int> vet;
inline int query(int l, int r, int *a, int *pa, int p) {
  int res = (a[r] - (long long)a[l - 1] * pa[r - l + 1]) % p;
  return res < 0 ? res + p : res;
}
void solve(int n) {
  pf[0] = pg[0] = 1, vet.clear(), res.clear();
  for (int i = 1; i <= n; i++) {
    pf[i] = (long long)pf[i - 1] * b % p1;
    pg[i] = (long long)pg[i - 1] * b % p2;
    f[i] = ((long long)f[i - 1] * b + s[i] - 'a') % p1;
    g[i] = ((long long)g[i - 1] * b + s[i] - 'a') % p2;
  }
  for (int i = n - 1; i >= 1; i--)
    if (query(1, i, f, pf, p1) == query(n - i + 1, n, f, pf, p1) && query(1, i, g, pg, p2) == query(n - i + 1, n, g, pg, p2)) vet.push_back(n - i);
  vet.push_back(n);
  int first = vet[0], delta = 0, cnt = 0;
  for (int i = 1; i < vet.size(); i++)
    if (!delta) {
      delta = vet[i] - vet[i - 1], cnt = 1;
    } else {
      if (vet[i] - vet[i - 1] == delta) ++cnt;
      else {
        res.push_back((atom){first, delta, cnt});
        first = vet[i], delta = 0, cnt = 0;
      }
    }
  res.push_back((atom){first, delta, cnt});
}
} // namespace h
void spfa(vector<int> &key, long long *dis) {
  static int l, r, q[N << 3];
  static bool inq[N];
  mod = *max_element(key.begin(), key.end());
  memset(dis, 63, mod << 3);
  q[l = r = 1] = dis[0] = 0;
  while (l <= r) {
    int u = q[l++];
    inq[u] = 0;
    for (int w : key) {
      int c = (u + w) / mod;
      int v = u + w - c * mod;
      if (dis[u] + c < dis[v]) {
        dis[v] = dis[u] + c;
        if (!inq[v]) inq[v] = 1, q[++r] = v;
      }
    }
  }
}
void trans(long long *f, long long *g, const atom &it) {
  if (!it.l) {
    for (int i = 0; i < mod; i++) {
      g[i] = min(f[i], f[(i - it.b + mod) % mod]);
    }
    return;
  }
  memset(t, 63, mod << 3), init();
  for (int i = 0; i < it.k; i++) st[i].reset(it.l);
  for (int i = -mod; i < mod; i++) {
    auto &st = ::st[(i + mod) % it.k];
    st.insert((i + mod) / it.k, i < 0 ? f[i + mod] + 1 : f[i]);
    if (i >= 0) t[i] = st.query();
  }
  for (int i = 0; i < mod; i++) {
    g[i] = min(f[i], i < it.b ? t[i - it.b + mod] + 1 : t[i - it.b]);
  }
}
int main() {
  for (cin >> T; T--; ans = 0, key.clear()) {
    scanf("%d%lld%s", &n, &w, s + 1);
    if (w < n) {
      puts("0");
      continue;
    }
    h::solve(n);
    for (auto x : res) {
      key.push_back(x.b);
      if (x.l) key.push_back(x.b + x.k * x.l);
    }
    spfa(key, dis[fl]);
    for (auto x : res) {
      trans(dis[fl], dis[fl ^ 1], x);
      fl ^= 1;
    }
    for (int i = 0; i < mod; i++)
      if (dis[fl][i] != 4557430888798830399) {
        ans += max(0ll, (w - n - i) / mod + 1 - dis[fl][i]);
      }
    printf("%lld\n", ans);
  }
}

评论

TABLE OF CONTENTS

题解
代码