1. 程式人生 > 實用技巧 >「NOI2015」品酒大會

「NOI2015」品酒大會

知識點: SA,並查集

原題面 Loj Luogu

「そして誰もいなくなるか?」


題意簡述

給定一字串 \(S\),位置 \(i\) 的屬性值為 \(a_i\)
定義位置 \(p,q\) 為「 \(r\) 相似」,當且僅當 \(S[p:p+r-1] = S[q:q+r-1]\) 成立。
特別地,對於任意 \(1\le p,q\le n, p\not ={q}\)\(p,q\) 都是「 \(0\) 相似」的。
求:選出兩個 「 \(0\sim n-1\) 相似」 的位置的 方案數,及選出兩個 「 \(0\sim n-1\) 相似」的位置屬性值 乘積的最大值


分析題意


程式碼實現

//知識點:SA,並查集
/*
By:Luckyblock
*/
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <ctype.h>
#include <set>
#define ll long long
const int kMaxn = 3e5 + 10;
//=============================================================
char S[kMaxn];
int n, m, a[kMaxn];
int cnt[kMaxn], id[kMaxn], rkid[kMaxn];
int sa[kMaxn], rk[kMaxn << 1], oldrk[kMaxn << 1], height[kMaxn];
ll ans1[kMaxn], ans2[kMaxn];
int fa[kMaxn], size[kMaxn];
std :: multiset <int> s[kMaxn];
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void GetMax(ll &fir, ll sec) {
  if (sec > fir) fir = sec;
}
bool cmp(int x, int y, int w) { //判斷兩個子串是否相等。
  return oldrk[x] == oldrk[y] && 
         oldrk[x + w] == oldrk[y + w]; 
}
void GetHeight() {
  for (int i = 1, k = 0; i <= n; ++ i) {
    if (rk[i] == 1) k = 0;
    else {
      if (k > 0) k --;
      int j = sa[rk[i] - 1];
      while (i + k <= n && j + k <= n && 
             S[i + k] == S[j + k]) {
        ++ k;
      }
    }
    height[rk[i]] = k;
  }
}
void SuffixSort() {
  m = 300;
  for (int i = 1; i <= n; ++ i) ++ cnt[rk[i] = S[i]];
  for (int i = 1; i <= m; ++ i) cnt[i] += cnt[i - 1];
  for (int i = n; i >= 1; -- i) sa[cnt[rk[i]] --] = i;
  for (int p, w = 1; w < n; w <<= 1) {
    p = 0;
    for (int i = n; i > n - w; -- i) id[++ p] = i;
    for (int i = 1; i <= n; ++ i) {
      if (sa[i] > w) id[++ p] = sa[i] - w;
    }
    memset(cnt, 0, sizeof (cnt));
    for (int i = 1; i <= n; ++ i) ++ cnt[(rkid[i] = rk[id[i]])];
    for (int i = 1; i <= m; ++ i) cnt[i] += cnt[i - 1];
    for (int i = n; i >= 1; -- i) sa[cnt[rkid[i]] --] = id[i];
    std ::swap(rk, oldrk);
    m = 0;
    for (int i = 1; i <= n; ++ i) {
      m += (cmp(sa[i], sa[i - 1], w) ^ 1);
      rk[sa[i]] = m;
    }
  }
  GetHeight();
}
bool Compare(int fir, int sec) {
  return height[fir] > height[sec];
}
int Find(int x) {
  return fa[x] == x ? x : fa[x] = Find(fa[x]);
}
void Union(int x, int y, int z) {
  x = Find(x), y = Find(y);
  if (size[x] < size[y]) std :: swap(x, y);
  ans1[z] += 1ll * size[x] * size[y];
  for (std :: set <int> :: iterator it = s[y].begin(); it != s[y].end(); ++ it) {
    s[x].insert(* it);
  }
  int t[5];
  t[1] = *s[x].begin(), t[2] = *(++ s[x].begin());
  t[3] = *(-- s[x].end()), t[4] = *(-- (-- s[x].end()));
  GetMax(ans2[z], std :: max(1ll * t[1] * t[2], 1ll * t[3] * t[4]));
  fa[y] = x;
  size[x] += size[y];
  if (s[x].size() > 5) {
    s[x].clear();
    for (int i = 1; i <= 4; ++ i) s[x].insert(t[i]);
  }
}
//=============================================================
int main() {
  n = read();
  scanf("%s", S + 1);
  for (int i = 1; i <= n; ++ i) a[i] = read();
  SuffixSort();
  memset(ans2, - 63, sizeof (ans2));
  for (int i = 2; i <= n; ++ i) id[i] = i;
  for (int i = 1; i <= n; ++ i) fa[i] = i;
  for (int i = 1; i <= n; ++ i) size[i] = 1;
  std :: sort(id + 2, id + n + 1, Compare);
  for (int i = 1; i <= n; ++ i) s[i].insert(a[i]);
  for (int i = 2; i <= n; ++ i) {
    Union(sa[id[i] - 1], sa[id[i]], height[id[i]]);
  }
  for (int i = n - 1; i >= 0; -- i) {
    ans1[i] += ans1[i + 1];
    GetMax(ans2[i], ans2[i + 1]);
  }
  for (int i = 0; i < n; ++ i) {
    printf("%lld %lld\n", ans1[i], ans1[i] ? ans2[i] : 0); 
  }
  return 0;
}