AcWing 1004. 品酒大会 (后缀数组,并查集)

题目链接

解题思路

??从后缀数组的角度考虑,利用\(lcp\)的性质,\(lcp(i, j) = min(lcp(k_1, k_2), i \neq k_1,k_2 \neq j\)。那么对于所有“r相似”的后缀来说他们之间的\(lcp\)肯定是大于等于\(r\)的。我们可以考虑根据lcp的大小按顺序将所有后缀加入集合,从而维护一系列lcp大于等于r的集合进而求得所有符合条件的后缀对数,这就是经典的并查集维护集合问题。

代码

const int maxn = 3e5+10;
char s[maxn];
int n, m;
int sa[maxn], x[maxn], y[maxn], c[maxn], rk[maxn], h[maxn];
void get_sa() {
    for (int i = 1; i<=n; ++i) ++c[x[i]=s[i]];
    for (int i = 2; i<=m; ++i) c[i] += c[i-1];
    for (int i = n; i; --i) sa[c[x[i]]--] = i;
    for (int k = 1; k<=n; k<<=1) {
        int num = 0;
        for (int i = n-k+1; i<=n; ++i) y[++num] = i;
        for (int i = 1; i<=n; ++i)
            if (sa[i]>k) y[++num] = sa[i]-k;
        for (int i = 1; i<=m; ++i) c[i] = 0;
        for (int i = 1; i<=n; ++i) ++c[x[i]];
        for (int i = 1; i<=m; ++i) c[i] += c[i-1];
        for (int i = n; i; --i) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
        swap(x, y);
        x[sa[1]] = 1, num = 1;
        for (int i = 2; i<=n; ++i) 
            x[sa[i]] = (y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]) ? num : ++num;
        if (num==n) break;
        m = num;
    }
}
vector<P> g[maxn];
void get_h() {
    for (int i = 1; i<=n; ++i) rk[sa[i]] = i;
    for (int i = 1, k = 0; i<=n; ++i) {
        if (rk[i]==1) continue;
        if (k) --k;
        int j = sa[rk[i]-1];
        while(i+k<=n && j+k<=n && s[i+k]==s[j+k]) ++k;
        h[rk[i]] = k;
        g[k].push_back({sa[rk[i]], sa[rk[i]-1]});
    }
}
int p[maxn];
ll sz[maxn], mx1[maxn], mx2[maxn], mi1[maxn], mi2[maxn];
int fd(int x) {
    return p[x]==x ? p[x]:p[x]=fd(p[x]);
}
ll sum = 0, ans = -2e18;
void calc(int x) {
    for (auto t :g[x]) {
        int fa = fd(t.x);
        int fb = fd(t.y);
        if (fa!=fb) {
            sum += 1LL*sz[fa]*sz[fb];
            p[fa] = fb;
            sz[fb] += sz[fa];
            if (mx1[fa]>=mx1[fb]) mx2[fb] = max(mx1[fb], mx2[fa]), mx1[fb] = mx1[fa];
            else if (mx1[fa]>mx2[fb]) mx2[fb] = mx1[fa];
            if (mi1[fa]<=mi1[fb]) mi2[fb] = min(mi1[fb], mi2[fa]), mi1[fb] = mi1[fa];
            else if (mi1[fa]<mi2[fb]) mi2[fb] = mi1[fa];
            ans = max({ans, 1LL*mx1[fb]*mx2[fb], 1LL*mi1[fb]*mi2[fb]});
        }
    }
}
int main() {
    IOS;
    cin >> n, m = 122;
    cin >> s+1;
    get_sa(); get_h();
    for (int i = 1; i<=n; ++i) {
        cin >> mx1[i]; 
        mi1[i] = mx1[i];
        mx2[i] = -2e18, mi2[i] = 2e18;
        p[i] = i, sz[i] = 1;
    }
    vector<P> t;
    for (int i = n-1; i>=0; --i) {
        calc(i);
        t.push_back({sum, ans<-1.5e18 ? 0:ans});
    }
    for (int i = t.size()-1; i>=0; --i) cout << t[i].x << ‘ ‘ << t[i].y << endl;
    return 0; 
}

AcWing 1004. 品酒大会 (后缀数组,并查集)

上一篇:Java 变量、常量


下一篇:Python模拟数据类型