「JSOI2015」串分割

传送门

首先我们会有一个贪心的想法:分得越均匀越好,因为长的绝对比短的大。

那么对于最均匀的情况,也就是 \(k | n\) 的情况,我们肯定是通过枚举第一次分割的位置,然后每一段长度 \(\frac{n}{k}\) 最后取最小的。

把这个思想运用到一般情况:如果分出来两段长短不一,那么长的只会比短的那个长度多 \(1\) ,再仔细想想,所有段只会有两种不同的长度 \(\lfloor \frac{n}{k} \rfloor, \lceil \frac{n}{k} \rceil\) ,那么我们就只需要判断什么情况下取长的,什么时候取短的就好了。

于是考虑二分最大的那个段的大小,然后再枚举第一次分割的位置,对于当前我们这段,如果分成长段会超过二分值,就换成短的,不然就按长的分,是否符合条件就判一下分出来的段的长度之和的大小,如果大于 \(n\) 说明当前的二分值大了,否则就是小了。

至于一段数的大小,我们可以直接将原数字环变成链然后倍长跑一遍后缀排序,比较的时候就直接拿 \(rank_i\) 来比就是了。

参考代码:

#include <algorithm>
#include <cstdio>
#define rg register
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
using namespace std;
template < class T > inline void read(T& s) {
s = 0; int f = 0; char c = getchar();
while ('0' > c || c > '9') f |= c == '-', c = getchar();
while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
s = f ? -s : s;
} const int _ = 4e5 + 5; int n, k, a[_], sa[_], rk[_]; inline void sufsort(int n) {
#define cmp(i, j, k) (y[i] == y[j] && y[i + k] == y[j + k])
static int x[_], y[_], cnt[_];
int m = 57;
for (rg int i = 1; i <= n; ++i) ++cnt[x[i] = a[i]];
for (rg int i = 1; i <= m; ++i) cnt[i] += cnt[i - 1];
for (rg int i = n; i >= 1; --i) sa[cnt[x[i]]--] = i;
for (rg int k = 1; k <= n; k <<= 1) {
int p = 0;
for (rg int i = n - k + 1; i <= n; ++i) y[++p] = i;
for (rg int i = 1; i <= n; ++i) if (sa[i] > k) y[++p] = sa[i] - k;
for (rg int i = 1; i <= m; ++i) cnt[i] = 0;
for (rg int i = 1; i <= n; ++i) ++cnt[x[i]];
for (rg int i = 1; i <= m; ++i) cnt[i] += cnt[i - 1];
for (rg int i = n; i >= 1; --i) sa[cnt[x[y[i]]]--] = y[i];
swap(x, y), x[sa[1]] = p = 1;
for (rg int i = 2; i <= n; ++i) x[sa[i]] = cmp(sa[i], sa[i - 1], k) ? p : ++p;
if (p == n) break ; else m = p;
}
for (rg int i = 1; i <= n; ++i) rk[sa[i]] = i;
} inline bool check(int mid) {
int x = n / k + (n % k != 0);
for (rg int i = 1; i <= x; ++i)
for (rg int p = i, j = 1; j <= k; ++j) {
p += rk[p] <= mid ? x : x - 1;
if (p >= i + n) return 1;
}
return 0;
} int main() {
#ifndef ONLINE_JUDGE
file("cpp");
#endif
read(n), read(k);
for (rg int i = 1; i <= n; ++i) scanf("%1d", a + i), a[n + i] = a[i];
sufsort(n << 1);
int l = 1, r = n << 1;
while (l < r) {
int mid = (l + r) >> 1;
if (check(mid)) r = mid; else l = mid + 1;
}
for (rg int i = 1; i <= n; ++i)
if (rk[i] == l) {
for (rg int j = 1; j <= n / k + (n % k != 0); ++j) putchar(a[i + j - 1] + '0'); break ;
}
return 0;
}

最新文章

  1. Oracle之DBMS_RANDOM包详解
  2. iOS:模态弹出窗控制器UIPopoverPresentationController
  3. [改善Java代码]用枚举实现工厂方法模式更简洁
  4. c#带参数和返回值的函数 开启线程调用的方法
  5. JSON C# Class Generator是一个从JSON文本中生成C#内的应用程序
  6. 浅谈Mybatis(一)
  7. php源码分析之PHPAPI宏的作用
  8. Ansible 入门 (1) - 安装和配置
  9. 学习MVC之租房网站(四)-实现Service层并进行单元测试
  10. memcached复制-repcached
  11. java面向对象基础(四):抽象类和接口
  12. Linux知识积累 (9) 创建用户、分配权限和更改所有者
  13. fastDFS 安装 配置 使用
  14. 网页的cdn引用地址,js,react,bootstrap
  15. 判断单向连通图(拓扑排序+tarjan缩点)
  16. OBS 录制视频 自己留存
  17. 【学习博客】Python学习初体验
  18. php .htaccess文件使用详解
  19. LOCAL_EXPORT_C_INCLUDES和LOCALC_INCLUDES 的差别
  20. priority_queue详解

热门文章

  1. Vue 嵌套路由使用总结
  2. Android Studio3.0.0之前首次安装通用配置
  3. java课后作业2
  4. not under version control
  5. VMware上Linux虚拟机和Windows共享文件夹
  6. 关于pip命令的几点提醒
  7. 第一个Mybatis项目
  8. java基础之 修饰符
  9. sql server和eclipse连接代码
  10. AcWing 907. 区间覆盖