「JSOI2015」串分割

「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;
}

原文地址:https://www.cnblogs.com/zsbzsb/p/12364662.html

时间: 2024-07-31 20:09:42

「JSOI2015」串分割的相关文章

「JSOI2015」salesman

「JSOI2015」salesman 传送门 显然我们为了使收益最大化就直接从子树中选大的就好了. 到达次数的限制就是限制了可以选的子树的数量,因为每次回溯上来都会减一次到达次数. 多种方案的判断就是看自己选中的子树中和没选的子树中是否存在两个值相等的,这样它们就可以通过互换来达到另一种方案,值得注意的是如果选了一个值为 \(0\) 的子树就肯定可以多一种方案出来,因为这颗子树选或不选都是满足最优的. 这里有个小问题:交到BZOJ上面去它会提示你 sort 没有声明,此时需要 #include

「JSOI2015」套娃

「JSOI2015」套娃 传送门 考虑贪心. 首先我们假设所有的套娃都互相不套. 然后我们考虑合并两个套娃 \(i\),\(j\) 假设我们把 \(i\) 套到 \(j\) 里面去,那么就可以减少 \(b_j \times out_i\) 的花费. 我们有一种 贪心策略就是说把所有套娃按 \(b\) 从大到小排序,然后每次找一个 \(out\) 最大的让它套. 我们可以这么证明正确性: 对于四个套娃 \(i, j, k, l\) ,假设 \(b_i > b_j, out_k > out_l\)

「JSOI2015」圈地

「JSOI2015」圈地 传送门 显然是最小割. 首先对于所有房子,权值 \(> 0\) 的连边 \(s \to i\) ,权值 \(< 0\) 的连边 \(i \to t\) ,然后对于所有的墙,连两条边,连接起墙两边的房子,容量就是修墙的费用,然后直接用权值和 - 最小割就是最大收益. 参考代码: #include <cstring> #include <cstdio> #define rg register #define file(x) freopen(x&qu

【bzoj4486】【JSOI2015】串分割

老省选题了. 首先考虑怎么比较超长数字的大小? 参见UTR1的那道题 先比size,然后比较字典序即可. 接下来考虑下切割的问题. 因为要将字符串切割成k份,所以这个字符串只会存在n/k个本质不同的起始位置. 然后可能会发现,如果能够整除的话,将这些起始位置直接后缀排序就可以了. 那么如果不能整除怎么办? 我们可以发现,如果有多余的,那么最长的字符串一定最多比别人多1 这个贪心的正确性比较的显然. 那么我们怎么处理长度不同的呢? 将之前的比较二分即可. #include<bits/stdc++.

「CJOJ2736」「POJ1014」大理石分割

Problem Description Marsha和Bill收藏了一些大理石.他们想要把这些大理石平均分配给两个人.如果大理石的价值一样,这将很容易做到,因为他们可以简单的对半分.不幸的是,一些大理石比其他的更大或者更漂亮.因此Marsha和Bill给每个大理石都规定了一个价值,它是一个1~6之间的自然数.现在他们想要把这些大理石分成两部分,每部分的价值之和相等.但他们很快意识到有可能不存在这样的分法(即使大理石的价值之和是偶数).例如,如果大理石的价值分别是1,1,3,4,4,就不可能被分为

字符串树「JSOI2015」

[题目描述] 萌萌买了一颗字符串树的种子,春天种下去以后夏天就能长出一棵很大的字符串树.字符串树很奇特,树枝上都密密麻麻写满了字符串,看上去很复杂的样子. 字符串树本质上还是一棵树,即N个节点N-1条边的连通无向无环图,节点从1到N编号.与普通的树不同的是,树上的每条边都对应了一个字符串.萌萌和JYY在树下玩的时候,萌萌决定考一考JYY.每次萌萌都写出一个字符串S和两个节点U,V,需要JYY立即回答U和V之间的最短路径(即,之间边数最少的路径.由于给定的是一棵树,这样的路径是唯一的)上有多少个字

「SCOI2012」喵星球上的点名

「SCOI2012」喵星球上的点名 填一个很久以前用 \(\texttt{AC}\) 自动机没填上的坑. 关于本题,能够通过本题的算法很多,这里作者采用的是后缀数组+树状数组的做法. 首先有一个显然的结论:若 \(s_2\) 是 \(s_1\) 的子串,则 \(s_1\) 一定存在一个后缀与 \(s_2\) 的最长公共前缀为 \(|s_2|\). 我们将读入的姓.名.询问串连成一个整体,形成一个字符串 \(s\),且在每一个姓.名.询问串中插入一个不存在文本中的字符,且保证询问串后插入的比姓名串

Linux 小知识翻译 - 「packet」(网络数据包)

用手机接收邮件或者访问网页的时候,一般会说有「packet费用」(这是日本的说法,在中国好像一般都说 "流量费"),即使对网络不太熟悉的人也知道「packet」这个词(这里也是日本的情况). 那么,「packet」到底是什么呢? 「packet」本义是「小包裹」的意思.在网络通信中,指的是 在传输时被分割的数据. 大的数据在传输时,被分割为多个小数据,这样数据在传输过程中就不会独占整个网络. 实际中,多个网络通信同时进行时,不会出现1个通信占用整个网络的情况,而是多个网络通信都在同时进

LOJ #2037. 「SHOI2015」脑洞治疗仪

#2037. 「SHOI2015」脑洞治疗仪 题目描述 曾经发明了自动刷题机的发明家 SHTSC 又公开了他的新发明:脑洞治疗仪——一种可以治疗他因为发明而日益增大的脑洞的神秘装置. 为了简单起见,我们将大脑视作一个 01 序列.1 代表这个位置的脑组织正常工作,0 代表这是一块脑洞. 1 0 1 0 0 0 1 1 1 0 脑洞治疗仪修补某一块脑洞的基本工作原理就是将另一块连续区域挖出,将其中正常工作的脑组织填补在这块脑洞中.(所以脑洞治疗仪是脑洞的治疗仪?) 例如,用上面第 8 号位置到第