嘘~ 正在从服务器偷取页面 . . .

CF477D Dreamoon and Binary 题解


CF477D Dreamoon and Binary 题解

题目链接:CF477D Dreamoon and Binary

题意

一天,cxy 在地上发现一个巨大的整数 \(x\) ,然后想把它写成二进制形式。

cxy 凭借她娴熟的技巧,将整数 \(x\) 变成了它的二进制形式。现在, 她想要用下列方式来打印这个数:

一开始, 她有一个整数 \(n=0\) ,然后, 她可以以任何顺序、次数没有限制地执行如下两种操作:

  1. \(n\) 打印到已经打印的字符串的右边,如果 \(n>0\), 则不打印前导 \(0\)
  2. \(n\) 自增 \(1\ (n \leftarrow n+1)\)

cxy 认为,如果一串操作序列可以成功地打印出(没有前导 0 的)\(x\) 且以操作1结束,则这串操作序列是理想的。cxy 想知道一共有多少种理想操作序列,以及最短的理想操作序列的长度。

由于答案可能很大,请模 \(10^9+7\) 输出。

输入格式

输入共一行,包含一个二进制形式表达的正整数 \(x(x < 2^{5000})\)

输出格式

第一行输出理想操作序列的个数模 \(10^9+7\) 的值。

第二行输出最短的理想操作序列的长度模 \(10^9+7\) 的值。

记原串为 \(x=\left(s_0 s_1 \cdots s_{n-1}\right)_2\) ,长度为 \(l\) ,接着考虑怎么dp。

\(f_{i,j}\) 表示 \(s\) 的前 \(i\) 位中,当前的 \(n = s[j..i]\) 的理想操作序列的个数,则 \[ f_{i, j}=\left[s_j=1\right] \times \sum_{\substack{k<j \\ s[k . . j] \leq s[j . . i]}} f_{j, k} \]\(g_{i,j}\) 表示 \(s\) 的前 \(i\) 位中,当前的 \(n = s[j..i]\) 的最短理想操作序列中操作1的个数,则 \[ g_{i, j}=\left[s_j=1\right] \times \left(\min _{\substack{k<j \\ s[k . . j] \leq s[j . . i]}}\left\{g_{j, k}\right\} + 1\right) \] 现在的复杂度是 \(\mathcal{O}(l^3)\) 的,于是考虑优化这个dp。其实这个不难想到,前缀和优化嘛 \[ F_{i, j}=\sum_{k=j}^{i-1} f_{i, k},~ G_{i,j} = \min_{j \le k \le i-1}g_{i,k} \] 则有 \(f_{i,j} = F_{j,k_0}\)\(k_0\) 为最小的满足 \(s[k_0..j] \le s[j..i]\)\(k_0\)\(G_{i,j}\) 同理。

判断 \(j-k=i-j\) 时的大小关系,只需要预处理 \(s\) 的任意两个后缀的最长公共前缀长度即可。

这样复杂度就讲到了 \(\mathcal{O}(l^2)\) 。第一问的答案是 \(\sum_{i = 0}^{l - 1}f_{l,i}\) ,第二问的答案需要仔细思考一下。

考虑任意一个使得 \(g_{l,i} < +\infty\)\(i\) ,可以计算出其 \(n\)\(n = s[i..l]\) ,然后加上 \(g_{l,i}\) 就是一个可行答案

显然当 \(l\) 较小时, \(n\) 的值会很大很大。不过容易证明当 \(n \ge 2^{31}\) 时,最小的操作个数一定当 \(n\) 最小时取到。

更严格的话其实是 \(n \ge 2^{17} > 5000\) ,也就是说最后一段分在 \(n-16\) 前增加的花费已经大于了 \(g\) 的最大值。

时间复杂度 \(\mathcal{O}(l^2)\)

代码:

#include <bits/stdc++.h>
using namespace std;
// #define int long long
typedef long long ll;
#define INF 0x7f7f7f7f
const ll mod = 1e9 + 7;
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
void add(int &x,int y) { (x += y) >= mod ? x -= mod : 0; }
#define N ((int)(5e3 + 5))
typedef int imat[N][N];

char s[N];
int n = 0,cnt = 0,mn = INF,tmp;
imat lcp, f, g, F, G;
bool cmp(int k, int j)
{
    int L = lcp[k][j]; if(k + L >= j) return 1;
    return s[k + L] <= s[j + L];
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> s; for(int i = 0; s[i]; i++) f[++n][0] = 1;
    for(int i = n - 1; ~i; i--)
        for(int j = n - 1; ~j; j--) lcp[i][j] = (s[i] == s[j] ? lcp[i + 1][j + 1] + 1 : 0);
    memset(g, 0x7f, sizeof(g)); memset(G, 0x7f, sizeof(G));
    for(int i = 1; i <= n; i++) G[i][0] = F[i][0] = g[i][0] = 1;
    for(int i = 2, k; i <= n; i++)
        for(int j = i - 1; ~j; j--)
        {
            if(s[j] == '1' && j)
            {
                k = (j << 1) - i; add(f[i][j], F[j][max(k + 1, 0)]);
                down(g[i][j], G[j][max(k + 1, 0)] + 1);
                if(k >= 0 && cmp(k, j))
                {
                    add(f[i][j], f[j][k]);
                    down(g[i][j], g[j][k] + 1);
                }
            }
            add(F[i][j] = F[i][j + 1], f[i][j]);
            down(G[i][j] = G[i][j + 1], g[i][j]);
        }
    for(int i = 0; i < n; i++) add(cnt, f[n][i]);
    for(int i = n - 1; ~i; i--)
    {
        if(tmp = 0, g[n][i] < INF)
        {
            for(int j = i; j < n; j++)
            {
                (tmp <<= 1) |= s[j] & 1;
                tmp >= mod ? tmp -= mod : 0;
            }
            add(tmp, g[n][i]);
            if(i < n - 30 && mn >= INF) { mn = tmp; break; }
            down(mn, tmp);
        }
    }
    cout << cnt << '\n' << mn << '\n';
    return 0;
}

参考文献

[1] https://yhx-12243.github.io/OI-transit/records/cf477D.html


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录