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

UOJ21 【UR #1】缩进优化 题解


UOJ21 【UR #1】缩进优化 题解

题目链接:#21. 【UR #1】缩进优化

题意

小 O 是一个热爱短代码的选手。在缩代码方面,他是一位身经百战的老手。世界各地的 OJ 上,很多题的最短解答排行榜都有他的身影。这令他感到十分愉悦。

最近,他突然发现,很多时候自己的程序明明看起来比别人的更短,实际代码量却更长。这令他感到很费解。经过一番研究,原来是因为他每一行的缩进都全是由空格组成的,大量的空格让代码量随之增大。

现在设小 O 有一份 $n$ 行的代码,第 $i$ 行有 $a_i$ 个空格作为缩进。

为解决这一问题,小 O 要给自己文本编辑器设定一个正整数的默认 Tab 宽度 $x$,然后对于每一行,编辑器从头至尾不断把连续 $x$ 个空格替换成一个 Tab,直到剩余空格数不足 $x$ 个。

最终缩进所占代码量为空格数与 Tab 数的和。请你帮小 O 选择一个合适的 $x$ ,使得缩进所占代码量最小。

输入格式

第一行包含一个正整数 $n(n \leq 10^6)$,表示代码行数。

接下来 $n$ 行,第 $i$ 行包含一个正整数 $a_i(a_i \leq 10^6)$,为初始时第 $i$ 行缩进的空格个数。

输出格式

输出一行一个整数,表示缩进所占代码量最小是多少。

C/C++ 输入输出 long long 时请用 %lld。C++ 可以直接使用 cin/cout 输入输出。

关键词:调和级数

简单推推柿子可以发现,我们要求的是下面这个东西的最小值

根据常见套路 $a_i \bmod T = a_i - T\left\lfloor{\frac{a_i}{T}}\right\rfloor$,有

显然前面是不变的,要最大化 $(T-1)\sum_{i=1}^{n}\left\lfloor\frac{a_i}{T}\right\rfloor$

显然 $T \in \left[1,\max\{a_i\}\right]$ ,考虑枚举 $T$ 。

注意到 $\left\lfloor\frac{a_i}{T}\right\rfloor$ 只有至多 $\left\lfloor\frac{\max\{a_i\}}{T}\right\rfloor$ 种取值,即

比如 $\forall a_i \in [T,2T-1],~\left\lfloor\frac{a_i}{T}\right\rfloor=1$ 。

暴力处理所有的区间 $[kT-T,kT-1]$ ,然后看看每一段区间里面有多少个 $a_i$ 。

然后分析一下时间复杂度。记 $M=\max\{a_i\}$ 。

对于每个枚举的 $T$ ,区间个数为 $\left\lfloor\frac{M}{T}\right\rfloor$ ,则

这就是调和级数。不过其实 $T=1$ 的情况不枚举也无所谓(

故时间复杂度 $O(M \log M)$(更精确点就是 $\Theta(M \ln M)$ 啦)

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdarg>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N ((int)(1e6+15))

int n,T,res,tmp,sum[N];
int calc(int l,int r)
{
    return (r > T ? sum[T] : sum[r]) - (l ? sum[l-1] : 0);
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n;
    for(int i=1,x; i<=n; i++)
        cin >> x, res+=x, T=max(T,x),++sum[x];
    for(int i=1; i<=T; i++) sum[i] += sum[i-1];
    for(int i=2; i<=T; i++)
    {
        int cur=0;
        for(int cnt=0, l=0, r=i-1; l<=T; l+=i, r+=i, ++cnt)
            cur += calc(l,r) * cnt;
        if(cur * (i-1) > tmp) tmp = cur * (i-1);
    }
    cout << (res - tmp) << '\n';
    return 0;
}

参考文献

[1] https://www.cnblogs.com/asuldb/p/11455826.html


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