题目
描述
小O是一个热爱短代码的选手。在缩代码方面,他是一位身经百战的老手。世界各地的OJ上,很多题的最短解答排行榜都有他的身影。这令他感到十分愉悦。
最近,他突然发现,很多时候自己的程序明明看起来比别人的更短,实际代码量却更长。这令他感到很费解。经过一番研究,原来是因为他每一行的缩进都全是由空格组成的,大量的空格让代码量随之增大。
现在设小O有一份 n 行的代码,第 i 行有 ai 个空格作为缩进。
为解决这一问题,小O要给自己文本编辑器设定一个正整数的默认TAB宽度 x,然后对于每一行,编辑器从头至尾不断把连续 x 个空格替换成一个TAB,直到剩余空格数不足 x 个。
最终缩进所占代码量为空格数与TAB数的和。请你帮小O选择一个合适的 x,使得缩进所占代码量最小。
输入格式
第一行一个正整数 n,表示代码行数。
接下来 n 行,第 i 行一个正整数 ai,为初始时第 i 行缩进的空格个数。
输出格式
一行一个整数,表示缩进所占代码量最小是多少。
C/C++ 输入输出 long long 时请用 %lld。C++ 可以直接使用 cin/cout 输入输出。
限制与约定
时间限制:1s
空间限制:256MB
这货可能没用过自动缩进吧
题解(参考官方题解)
自己思考过程
第一步暴力枚举,发现没有啥规律,不满足二分性
第二步尝试暴力做,发现对每个数字可以优化的最大 tab 上限就是 \sqrt{a_i}
第三步看题解
第四步 看完来自己写一个题解
来解解吧
考虑 的算法
不难发现,自己想根本想不出
那就考虑一下优化吧
第一步 sort+unique
这样对于相同的数字操作就不会重复了
然后考虑一下计数方式
假如我们用 记录 的答案
那么每次 的时候,我们都需要在 上加一个 ,每个数字实际操作仍为
反过来一下,我们在赋初值时使每一个 都为
对于 ,我们每次都更新答案,减去可以减少的空格数
对于 ,我们则不做操作
这样就目前来看操作复杂度为
考虑枚举过程
容易发现,对于排序过后相邻的 ,易有
若 的个数为 ,那么则有
若对于 ,有
可用前缀和快速处理
找出最大的 ,令其为
那么每一次对 枚举 时, 上限就是
所以我们选择枚举 ,再枚举 ,每一次枚举 都是
所以总的复杂度就是
可以说是非常优秀了
附上代码
#include#include #define getchar() *inp++using namespace std;char INP[1<<25],*inp=INP;inline int input(){ char c=getchar();int o; while(c>57||c<48)c=getchar(); for(o=0;c>47&&c<58;c=getchar())o=(o<<1)+(o<<3)+c-48; return o;}long long MA,res,ALL,ans[1000123],C[1000123];int main(){ //freopen("In.in","r",stdin); fread(INP,1,1<<25,stdin); int n=input();long long Ai; for(int i=1;i<=n;i++) Ai=input(),MA=max(MA,Ai),C[Ai]++,ALL+=Ai; for(int i=1;i<=MA;i++)C[i]+=C[i-1]; res=ALL; for(int t=2;t<=MA;t++) { ans[t]=ALL; int l=0,r; for(long long k=0;k<=MA/t;k++) { r=min((k+1)*t-1,MA); ans[t]-=(C[r]-C[l])*k*(t-1); l=r; } if(res>ans[t])res=ans[t]; } printf("%lld",res);}复制代码