题解:CF1968G2 Division + LCP (hard version)
https://www.luogu.com.cn/problem/CF1968G2
CF1968G2 Division + LCP (hard version) 题解
前言
这题可以
如果读题解有些抽象的话可以看代码辅助理解。
题意转化
由于是从第一个位置开始分段,所以问题就是在字符串中,选出
思路
Easy Version
如果只用求对一个
可以二分答案,然后在内部可以
check 如何
时间复杂度
Hard Version
想了很多种做法发现都不好做,猜一猜时间复杂度,假设时间复杂度带根号,没想到完全可做。
Hard Version 就是有多个
- 对于
的 ,用 Easy Version 的做法求解,时间复杂度 。 - 对于
的 ,则分段后最长的段最长 ,所以其答案必定小于等于 。- 枚举每个长度
,使用 Easy Version 中 check 的做法来求最多可以分几段,然后前缀和一下。 - 时间复杂度
。
- 枚举每个长度
两种做法合并起来即可。
优化1
发现两种情况处理的时间复杂度不同,一个
显然第一种做法给
这个
优化2
对于第一种做法,发现
优化3
对于第一种做法,发现答案有很长一段连续,所以在二分前先 check
Code
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
using Pair = pair<LL, LL>;
const int MAXN = 2e5 + 3;
const LL mod = 998244353;
const Pair B = {37, 47}; // 使用双哈希
int n, L, R, ans[MAXN];
char s[MAXN];
Pair Hash[MAXN], Bpow[MAXN];
Pair H(int l, int r){ // 求区间哈希值
return {(Hash[r].first - Hash[l-1].first * Bpow[r-l+1].first % mod + mod) % mod,
(Hash[r].second- Hash[l-1].second* Bpow[r-l+1].second% mod + mod) % mod};
}
bool check(int D, int k){ // 二分中的 check 函数
int cnt = 0;
for(int i = 1; i + D - 1 <= n; ){
while(i + D - 1 <= n && H(i, i + D - 1) != H(1, D)) i++;
cnt += i + D - 1 <= n && H(i, i + D - 1) == H(1, D), i = i + D;
if(cnt >= k) return 1;
}
return 0;
}
void work(){
cin >> n >> L >> R;
for(int i = 1; i <= n; i++){
cin >> s[i];
}
Bpow[0] = {1, 1};
for(int i = 1; i <= n; i++){ // 预处理前缀哈希
Bpow[i] = {Bpow[i-1].first * B.first % mod, Bpow[i-1].second* B.second% mod};
Hash[i] = {(Hash[i-1].first * B.first % mod + s[i] - 'a' + 1) % mod,
(Hash[i-1].second* B.second% mod + s[i] - 'a' + 1) % mod};
}
for(int i = 1; i <= n; i++) ans[i] = 0;
for(int D = min(n, 1000); D >= 1; D--){ // 求解 200 < k <= n 的答案
int mx = 0;
for(int i = 1; i + D - 1 <= n; ){ // 类似 check 函数
while(i + D - 1 <= n && H(i, i + D - 1) != H(1, D)) i++;
mx += i + D - 1 <= n && H(i, i + D - 1) == H(1, D), i = i + D;
}
ans[mx] = max(ans[mx], D); // 前缀和
}
for(int i = n - 1; i >= 1; i--) ans[i] = max(ans[i], ans[i + 1]);
ans[0] = n;
for(int k = 1; k <= min(n, 200); k++){ // 求解 1 <= k <= 200 的答案
int l = 0, r = ans[k - 1];
if(check(r, k)) l = r;
while(l < r){
int mid = (l + r + 1) >> 1;
if(check(mid, k)){
l = mid;
}else r = mid - 1;
}
ans[k] = max(ans[k], l);
}
for(int i = L; i <= R; i++) cout << ans[i] << " ";
cout << "\n";
}
int main(){
ios::sync_with_stdio(0), cin.tie(0);
int T;
cin >> T;
while(T--) work();
return 0;
}
本文作者:hhhqx
本文链接:https://www.cnblogs.com/huangqixuan/p/18584539
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步