2021 CCPC 桂林站 補題
阿新 • • 發佈:2021-11-17
好忙,不過題還是得補,欠了好多了已經。
J Suffix Automaton
場上沒看見第一關鍵字是長度,還跟隊友反覆確認是不是本質不同,還剩17分鐘rush了一個,結果發現樣例都過不去,成功沒寫完這個F。
如果按照第一關鍵字為長度排序的話依然可以二分出答案的長度。字尾排序之後一個字尾會貢獻 \([ht_i + 1, n - sa_i + 1]\) 區間內長度的串,所以我們可以將詢問離線下來使用一個權值線段樹來查詢 Kth,需要注意的是查詢出來的那個位置是 字尾排序後 最靠前的那個位置,而不是 原序列 中最靠前的那個位置。如果我們得到了字尾排序後的那個位置 \(p\) ,相當於還需要維護 \(ht\)
這麼看下來字尾陣列做挺麻煩的,相當於將一個題拆成了兩個題做。
另一件比較無奈的事情是,如果暴力開主席樹線上做的話空間是有點開不下的……
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef double db; typedef pair <int, int> pin; #ifndef ONLINE_JUDGE bool MEMORY_ST; #endif const int N = 1e6 + 5; const ll P = 998244353LL; int n, qn, trueQn; int sa[N], rk[N], oldrk[N << 1], id[N], px[N], cnt[N], ht[N]; char str[N]; int ufs[N], mn[N], ans[N][2]; ll cntSum[N]; struct InsNode { int id; ll v; /* v = 1: ins */ /* v = -1: del */ }; vector <InsNode> vec[N]; struct QueryNode1 { int id, len; ll k; }; vector <QueryNode1> q1[N]; struct QueryNode2 { int id, len, pos; }; vector <QueryNode2> q2[N]; inline bool cmp(int x, int y, int w) { return oldrk[x] == oldrk[y] && oldrk[x + w] == oldrk[y + w]; } inline void saInit() { memset(cnt, 0, sizeof(cnt)); int m = 200, p; for (int i = 1; i <= n; i++) ++cnt[rk[i] = str[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 w = 1; w < n; w <<= 1, m = p) { 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[px[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[px[i]]--] = id[i]; memcpy(oldrk, rk, sizeof(rk)); p = 0; for (int i = 1; i <= n; i++) rk[sa[i]] = cmp(sa[i], sa[i - 1], w) ? p : ++p; } for (int i = 1; i <= n; i++) rk[sa[i]] = i; for (int i = 1, k = 0; i <= n; i++) { if (k) --k; for (; str[i + k] == str[sa[rk[i] - 1] + k]; ++k); ht[rk[i]] = k; } } namespace SegT { ll s[N << 2]; #define lc (p << 1) #define rc (p << 1 | 1) #define mid ((l + r) >> 1) inline void up(int p) { s[p] = s[lc] + s[rc]; } void modify(int p, int l, int r, int x, ll v) { if (l == r) { s[p] += v; return; } if (x <= mid) modify(lc, l, mid, x, v); else modify(rc, mid + 1, r, x, v); up(p); } int query(int p, int l, int r, ll k) { if (l == r) return l; ll cur = s[lc]; if (k > cur) return query(rc, mid + 1, r, k - cur); else return query(lc, l, mid, k); } #undef mid } using namespace SegT; inline void solve1() { for (int i = 1; i <= n; i++) { for (auto insNode : vec[i]) { modify(1, 1, n, insNode.id, insNode.v); } for (auto qryNode : q1[i]) { int res = query(1, 1, n, qryNode.k); // printf("%d %d\n", qryNode.id, sa[res]); q2[qryNode.len].emplace_back((QueryNode2) {qryNode.id, qryNode.len, res}); } } } int get(int x) { return ufs[x] == x ? x : ufs[x] = get(ufs[x]); } inline void merge(int x, int y) { int fx = get(x), fy = get(y); if (fx == fy) return; ufs[fx] = fy; mn[fy] = min(mn[fy], mn[fx]); } vector <int> htNode[N]; inline void solve2() { for (int i = 1; i <= n; i++) { ufs[i] = i; mn[i] = sa[i]; } for (int i = 2; i <= n; i++) htNode[ht[i]].emplace_back(i); for (int i = n; i >= 1; i--) { for (auto curHtNode : htNode[i]) { merge(curHtNode - 1, curHtNode); } for (auto qryNode : q2[i]) { int res = mn[get(qryNode.pos)]; ans[qryNode.id][0] = res, ans[qryNode.id][1] = res + qryNode.len - 1; } } } #ifndef ONLINE_JUDGE bool MEMORY_ED; #endif int main() { #ifndef ONLINE_JUDGE freopen("sample.in", "r", stdin); clock_t st_clock = clock(); #endif scanf("%s", str + 1); n = strlen(str + 1); saInit(); // assert(ht[1] == 0); ll tot = 0; for (int i = 1; i <= n; i++) { tot += n + 1 - ht[i] - sa[i]; ++cntSum[ht[i] + 1]; --cntSum[n - sa[i] + 1 + 1]; vec[ht[i] + 1].emplace_back((InsNode) {i, 1}); vec[n - sa[i] + 1 + 1].emplace_back((InsNode) {i, -1}); // printf("%d %d\n", ht[i] + 1, n - sa[i] + 1); } for (int i = 1; i <= n; i++) cntSum[i] += cntSum[i - 1]; for (int i = 1; i <= n; i++) cntSum[i] += cntSum[i - 1]; // for (int i = 1; i <= n; i++) // printf("%lld%c", cntSum[i], " \n"[i == n]); // printf("%lld\n", tot); scanf("%d", &qn); trueQn = 0; for (int i = 1; i <= qn; i++) { ll k; scanf("%lld", &k); if (k > tot) { ans[i][0] = ans[i][1] = -1; } else if (k == tot) { ans[i][0] = 1, ans[i][1] = n; } else { ++trueQn; int l = 0, r = n, mid, res = -1; for (; l <= r; ) { mid = (l + r) / 2; if (cntSum[mid] < k) l = mid + 1, res = mid; else r = mid - 1; } assert(res < n && res >= 0); q1[res + 1].emplace_back((QueryNode1) {i, res + 1, k - cntSum[res]}); } } solve1(); solve2(); for (int i = 1; i <= qn; i++) printf("%d %d\n", ans[i][0], ans[i][1]); #ifndef ONLINE_JUDGE clock_t ed_clock = clock(); printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000); printf("memory = %.2f MB\n", (&MEMORY_ED - &MEMORY_ST) / 1024.0 / 1024.0); #endif return 0; }