QOJ.ac

QOJ

IDProblemSubmitterResultTimeMemoryLanguageFile sizeSubmit timeJudge time
#890984#5368. 异世界的文章分割者Cyanmond0 0ms15444kbC++237.1kb2025-02-09 07:08:102025-02-09 07:08:11

Judging History

This is the latest submission verdict.

  • [2025-02-09 07:08:11]
  • Judged
  • Verdict: 0
  • Time: 0ms
  • Memory: 15444kb
  • [2025-02-09 07:08:10]
  • Submitted

answer

#include <bits/stdc++.h>

using namespace std;

// #include "angel/math/modint.hpp"

// clang-format off
using ll = long long;
template <class T>
constexpr int len(const T &c) {
    return int(std::size(c));
}
#define L(i, l, r) for (int i = (l); i < (r); ++i)
#define R(i, l, r) for (int i = (r) - 1; i >= (l); --i)
#define ALL(x) (x).begin(), (x).end()
#define fi first
#define se second
// clang-format on

struct State {
    int len;
    int link;
    int firstTime = 1 << 30;
    int lastTime = -1;
    array<int, 26> nxtNodes;
    State() {
        fill(ALL(nxtNodes), -1);
    }
} stateDefault;

array<State, 100000> nodes;
array<int, 50000> inDeg;

struct SuffixAutomaton {
    int n = 0;
    int last = 0;

    SuffixAutomaton() {
        init();
    }

    SuffixAutomaton(string s) {
        fill(nodes.begin(), nodes.begin() + 2 * len(s), stateDefault);
        fill(inDeg.begin(), inDeg.begin() + len(s), 0);
        init();
        L(i, 0, len(s)) {
            extend(s[i], i);
        }
        acm();
    }

    void init() {
        // nodes.push_back(State());
        nodes[0].len = 0;
        nodes[0].link = -1;
        n = 1;
    }

    void extend(char cx, int t) {
        const int c = cx - 'a';
        int cur = n++;
        int p = last;
        // nodes.push_back(State());
        nodes[cur].len = nodes[last].len + 1;
        nodes[cur].firstTime = nodes[cur].lastTime = t;

        while (p != -1 and nodes[p].nxtNodes[c] == -1) {
            nodes[p].nxtNodes[c] = cur;
            p = nodes[p].link;
        }

        if (p == -1) {
            nodes[cur].link = 0;
        } else {
            const int q = nodes[p].nxtNodes[c];
            if (nodes[p].len + 1 == nodes[q].len) {
                nodes[cur].link = q;
            } else {
                int clone = n++;
                // nodes.push_back(State());
                nodes[clone].len = nodes[p].len + 1;
                nodes[clone].link = nodes[q].link;
                nodes[clone].nxtNodes = nodes[q].nxtNodes;
                while (p != -1 and nodes[p].nxtNodes[c] == q) {
                    nodes[p].nxtNodes[c] = clone;
                    p = nodes[p].link;
                }
                nodes[q].link = nodes[cur].link = clone;
            }
        }

        last = cur;
    }

    void acm() {
        // essentially tree dp
        queue<int> que;
        L(i, 0, n) {
            if (nodes[i].link == -1) continue;
            ++inDeg[nodes[i].link];
        }
        L(i, 0, n) if (inDeg[i] == 0) que.push(i);
        while (not que.empty()) {
            const int f = que.front();
            que.pop();
            const int p = nodes[f].link;
            if (p == -1) continue;
            --inDeg[p];
            if (inDeg[p] == 0) {
                que.push(p);
            }
            nodes[p].firstTime = min(nodes[p].firstTime, nodes[f].firstTime);
            nodes[p].lastTime = max(nodes[p].lastTime, nodes[f].lastTime);
        }
    }
};

void solve() {
    int n, k;
    cin >> n >> k;
    string s;
    cin >> s;

    auto calcValue = [&](int l, int r) -> ll {
        if (r > n) {
            return 1ll << 61;
        }
        const auto S = s.substr(l, r - l);
        const int m = len(S);
        SuffixAutomaton sa(S);

        vector<ll> imos1(m), imos2(m + 1);
        L(i, 0, sa.n) {
            auto &node = nodes[i];
            if (node.link == -1) continue;
            const ll minLen = nodes[node.link].len + 1;
            const ll maxLen = node.len;
            // len : [minLen, maxLen]
            const ll firstTime = node.firstTime;
            const ll lastTime = node.lastTime;
            // cerr << __LINE__ << ' ' << minLen << " " << maxLen << " " << firstTime << " " << lastTime << endl;

            // [firstTime, lastTime - maxLen]: + (maxLen - minLen + 1)
            // [lastTime - maxLen, lastTime - maxLen + i]: + (minLen - firstTime) - i
            if (firstTime <= lastTime - maxLen) {
                imos1[firstTime] += maxLen - minLen + 1;
                imos2[lastTime - maxLen + 1] -= 1;
                imos2[lastTime - minLen + 2] += 1;
            } else if (lastTime - firstTime >= minLen) {
                ll mxLen = lastTime - firstTime;
                imos1[lastTime - mxLen] += mxLen - minLen + 1;
                imos2[lastTime - mxLen + 1] -= 1;
                imos2[lastTime - minLen + 2] += 1;
            }
        }

        L(i, 1, m + 1) imos2[i] += imos2[i - 1];
        L(i, 0, m) imos1[i] += imos2[i];
        L(i, 1, m) imos1[i] += imos1[i - 1];

        ll score = 0;
        L(i, 0, m - 1) {
            score += min(__int128(1ll << 61), __int128(imos1[i]) * __int128_t(imos1[i]));
            if (score > (1ll << 60)) break;
        }
        return score;

        // sa から T[i:r-1] の文字列を数え上げ
        /*
        もともと 2 文字列だったらどうやるか?
        S の suffix automaton を作成 O(|S|)
        i = 1, 2, ..., |T| について
            最大の j であって T[j:i] in S であるようなものを見つける
        i -> i+1 transition
        v, lを持つ
        v から T[i+1] character の nxtNode がある場合そこをたどる
            v = nxtNode
            l = l + 1
        ない場合、 link を辿る (前のいくつかを消して) until T[i+1] nxtNode が見つかる
            l = v.len

        答えは ?
        Node は全て unique な substrings の表現だと思い出す
        実際には、 nxtNode を辿ったとき前にも拡張されている場合がある
        それぞれの node について l の max を持つ。
        sum(min(l - node[v.link].len, v.len - v.link.len)) for all nodes?
        */

        /*
        実際には S[1:i] と S[i+1:n] の共通部分文字列の個数をそれぞれの i について求める
        当然 S[1:i] の方を Suffix Automaton で管理したい
        i -> i+1 での変化はどうなるか

        それぞれのノードについて、最初の出現時刻、最後の出現時刻を持つ
        */
    };

    auto judge = [&](const ll threshold) -> bool {
        int l = 0;
        int cntRanges = 0;
        while (l != n) {
            if (cntRanges > k) break;
            int r = l;
            L(k, 0, 20) {
                if (calcValue(l, l + (1 << k)) > threshold) {
                    R(i, 0, k) {
                        if (calcValue(l, r + (1 << i)) <= threshold) {
                            r += 1 << i;
                        }
                    }
                    break;
                }
            }

            l = r;
        }

        return cntRanges <= k;
    };

    ll ok = 1ll << 60, ng = -1;
    while (ok - ng > 1) {
        const auto mid = (ok + ng) / 2;
        if (judge(mid)) {
            ok = mid;
        } else {
            ng = mid;
        }
    }
    cout << ok << endl;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t = 1;
    // cin >> t;
    while (t--) {
        solve();
    }
}

Details

Tip: Click on the bar to expand more detailed information

Subtask #1:

score: 0
Wrong Answer

Test #1:

score: 0
Wrong Answer
time: 0ms
memory: 15444kb

input:

10 3
aaaaaaaaaa

output:

0

result:

wrong answer 1st lines differ - expected: '6', found: '0'

Subtask #2:

score: 0
Skipped

Dependency #1:

0%

Subtask #3:

score: 0
Skipped

Dependency #1:

0%

Subtask #4:

score: 0
Skipped

Dependency #1:

0%

Subtask #5:

score: 0
Skipped

Dependency #1:

0%