QOJ.ac
QOJ
ID | Problem | Submitter | Result | Time | Memory | Language | File size | Submit time | Judge time |
---|---|---|---|---|---|---|---|---|---|
#890984 | #5368. 异世界的文章分割者 | Cyanmond | 0 | 0ms | 15444kb | C++23 | 7.1kb | 2025-02-09 07:08:10 | 2025-02-09 07:08:11 |
Judging History
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%