20240705

forgotmyhandle發表於2024-07-07

T1

NFLSOJ P5030 最小表示

考慮兩個串本質相同的條件,發現如果計算出每一位上的字母距離它上一次出現的距離 \(dis_i\),那兩個串本質相同等價於所有 \(dis_i\) 相同。注意到這個東西只和相對位置有關,所以只需要先對原串求一遍 \(dis\) 陣列,然後對這個 \(dis\) 陣列字尾排序一下,求出本質不同子串數即可。

但是這個事情很顯然是不對的,因為比如說對於 \(\texttt{abcabcbac}\),對原串求 \(dis\) 得到 \(\{ 1, 2, 3, 2, 2, 2, 1, 3, 2 \}\),而如果考慮 \(\texttt{cbac}\) 這個字尾,\(dis\) 就變成了 \(\{ 1, 2, 3, 2 \}\)。容易發現這裡對應位置上的 \(dis\) 值發生了變化。這實際上是由於字尾的起始點分隔了某些字母的相鄰兩次出現,從而導致後面那次出現的 \(dis\) 發生變化。注意到如果把這些發生變化的 \(dis\) 值全部變成 \(0\),也不會影響子串同構的判斷。因此對於每個字尾直接把這些位置歸零,然後把處理之後的字尾做字尾排序。注意,這裡每個字尾是獨立處理的,它們各自獨立將自己的對應位置歸零。

接下來考慮如何對處理之後的字尾做字尾排序。由於每個字尾是獨立更改的,我們並不能很容易地完成排序。但是注意到字母只有 \(10\) 個,也就是 \(0\) 位置至多隻有 \(10\) 個。因此可以對每個字尾記錄該字尾的所有 \(0\) 位置,然後使用二分 + 雜湊比較兩個子串的字典序。如果沒有刪除位置,直接雜湊即可。對於刪除位置,由於數量很小,暴力列舉所有刪除位置,將這一位對於雜湊值的貢獻減去即可。

完成字尾排序之後,直接根據 SA 求本質不同子串數的套路拿總共的減去每兩個排名相鄰的字尾的 LCP 即可。

程式碼
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
const int B = 233333;
const int P = 1000000007;
int a[50005], n, m;
string str;
int ap[15], aft[50005][15];
int del[50005][15];
int hsh[50005];
int pw[50005], lst[50005];
int ord[50005];
int Query(int l, int r) { return (P + hsh[r] - hsh[l - 1] * pw[r - l + 1] % P) % P; }
int chk(int l, int r) {
    int ret = Query(l, r);
    for (int i = 1; i <= del[l][0]; i++) {
        if (del[l][i] <= r) 
            ret = (P + ret - pw[r - del[l][i]] * a[del[l][i]] % P) % P;
    }
    return ret;
}
int lcp(int a, int b) {
    int l = 1, r = n - max(a, b) + 1, mid, ans = 0;
    while (l <= r) {
        mid = (l + r) >> 1;
        if (chk(a, a + mid - 1) == chk(b, b + mid - 1)) 
            ans = mid, l = mid + 1;
        else 
            r = mid - 1;
    }
    return ans;
}
int q(int x, int y) {
    int ax = a[y];
    for (int i = 1; i <= del[x][0]; i++) del[x][i] == y ? (ax = 0) : 0;
    return ax;
}
bool cmp(int a, int b) {
    int ans = lcp(a, b);
    if (ans == n - max(a, b) + 1) 
        return a > b;
    else 
        return q(a, a + ans) < q(b, b + ans);
}
signed main() {
    freopen("string.in", "r", stdin);
    freopen("string.out", "w", stdout);
    cin >> n >> m;
    pw[0] = 1;
    for (int i = 1; i <= n; i++) pw[i] = pw[i - 1] * B % P;
    cin >> str;
    str = ' ' + str;
    for (int i = 1; i <= n; i++) {
        a[i] = i - ap[str[i] - 'a'];
        hsh[i] = (hsh[i - 1] * B + a[i]) % P;
        lst[i] = ap[str[i] - 'a'];
        ap[str[i] - 'a'] = i;
        for (int j = lst[i] + 1; j <= i; j++) aft[j][str[i] - 'a'] = i;
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < m; j++) {
            if (aft[i][j]) 
                del[i][++del[i][0]] = aft[i][j];
        }
    }
    for (int i = 1; i <= n; i++) ord[i] = i;
    sort(ord + 1, ord + n + 1, cmp);
    int ans = n * (n + 1) / 2;
    for (int i = 1; i < n; i++) ans -= lcp(ord[i], ord[i + 1]);
    cout << ans << "\n";
    return 0;
}

T3

NFLSOJ P3361 模板性二維數點

考慮所有黑點的縱座標都小於白點的縱座標時會發生什麼。此時我們選出的答案裡必然包含黑點權值的最大值或白點權值的最大值。如果兩個點可以同時選,則顯然正確。否則對於任意一組解,把其中一個換成對應的權值最大值必然更優。

因此考慮對縱座標分治,每次考慮縱座標在分治中心之下的黑點和縱座標在分治中心之上的白點,根據結論我們可以在這一層中選出 \(O(區間長度)\) 個有效(可能成為答案)的點對。分治有 \(O(\log n)\) 層,因此有用點對的級別在 \(O(n \log n)\)。這樣就可以對每個詢問離線做二維數點了。

程式碼
#include <iostream>
#include <algorithm>
#include <cassert>
#include <vector>
#include <map>
#define lowbit(x) ((x) & (-(x)))
using namespace std;
int n, q;
struct Point {
    int x, y, w;
} val[3000005], a[100005], b[100005];
int vcnt;
struct Query {
    int l, r, id;
} qs[500005];
vector<Point> va;
vector<Point> vb;
int d[1200005], dcnt;
map<int, int> mp;
void Solve(int l, int r, vector<Point> v1, vector<Point> v2) {
    if (v1.empty() || l >= r) 
        return;
    int mid = (l + r) >> 1;
    vector<Point> va1, va2, vb1, vb2;
    for (auto p : v1) (p.y <= mid ? va1 : va2).emplace_back(p);
    for (auto p : v2) (p.y <= mid ? vb1 : vb2).emplace_back(p);
    if (va1.size() && vb2.size()) {
        Point p1, p2;
        p1.w = p2.w = 0;
        for (auto p : va1) p.w > p1.w ? (p1 = p) : p;
        for (auto p : vb2) {
            p.w > p2.w ? (p2 = p) : p;
            val[++vcnt] = (Point) { p1.x, p.x, p1.w + p.w };
        }
        for (auto p : va1) p.x != p1.x ? (val[++vcnt] = (Point) { p.x, p2.x, p.w + p2.w }) : p1;
    }
    Solve(l, mid, va1, vb1);
    Solve(mid + 1, r, va2, vb2);
}
struct BIT {
    int bit[1000005];
    void add(int x, int y) { for (; x <= 1000000; x += lowbit(x)) bit[x] = max(bit[x], y); }
    int query(int x) {
        int ret = 0;
        for (; x; x -= lowbit(x)) ret = max(ret, bit[x]);
        return ret;
    }
} bit;
struct SBIT {
    int bit[1000005];
    void add(int x, int y) { for (; x; x -= lowbit(x)) bit[x] = max(bit[x], y); }
    int query(int x) {
        int ret = 0;
        for (; x <= 1000000; x += lowbit(x)) ret = max(ret, bit[x]);
        return ret;
    }
} bit2;
int ans[500005];
int main() {
    freopen("template.in", "r", stdin);
    freopen("template.out", "w", stdout);
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i].x >> a[i].y >> a[i].w;
    }
    for (int i = 1; i <= n; i++) {
        cin >> b[i].x >> b[i].y >> b[i].w;
    }
    cin >> q;
    for (int i = 1; i <= q; i++) cin >> qs[i].l >> qs[i].r, qs[i].id = i;
    for (int i = 1; i <= n; i++) d[++dcnt] = a[i].x, d[++dcnt] = b[i].x;
    for (int i = 1; i <= q; i++) d[++dcnt] = qs[i].l, d[++dcnt] = qs[i].r;
    d[++dcnt] = 0;
    sort(d + 1, d + dcnt + 1);
    d[0] = 2147483647;
    mp[d[0]] = -n - q - 1;
    for (int i = 1; i <= dcnt; i++) d[i] != d[i - 1] ? (mp[d[i]] = mp[d[i - 1]] + 1) : 0;
    for (int i = 1; i <= n; i++) a[i].x = mp[a[i].x], b[i].x = mp[b[i].x];
    for (int i = 1; i <= q; i++) qs[i].l = mp[qs[i].l], qs[i].r = mp[qs[i].r];
    dcnt = 0;
    for (int i = 1; i <= n; i++) d[++dcnt] = a[i].y, d[++dcnt] = b[i].y;
    sort(d + 1, d + dcnt + 1);
    mp.clear();
    for (int i = 1; i <= dcnt; i++) d[i] != d[i - 1] ? (mp[d[i]] = mp[d[i - 1]] + 1) : 0;
    for (int i = 1; i <= n; i++) a[i].y = mp[a[i].y], b[i].y = mp[b[i].y];
    for (int i = 1; i <= n; i++) {
        va.emplace_back(a[i]);
        vb.emplace_back(b[i]);
    }
    Solve(1, n * 2, va, vb);
    sort(val + 1, val + vcnt + 1, [](Point a, Point b) { return a.x < b.x; });
    sort(qs + 1, qs + q + 1, [](Query a, Query b) { return a.l < b.l; });
    for (int i = 1, j = 1; i <= q; i++) {
        while (j <= vcnt && val[j].x < qs[i].l) bit2.add(val[j].y, val[j].w), ++j;
        ans[qs[i].id] = bit2.query(qs[i].r);
    }
    for (int i = q, j = vcnt; i; i--) {
        while (j > 0 && val[j].x > qs[i].l) bit.add(val[j].y, val[j].w), --j;
        ans[qs[i].id] = max(ans[qs[i].id], bit.query(qs[i].r));
    }
    for (int i = 1; i <= q; i++) cout << (ans[i] == 0 ? -1 : ans[i]) << "\n";
    return 0;
}

考慮可能對答案有貢獻的元素。

相關文章