博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
UOJ #22 缩进优化
阅读量:6259 次
发布时间:2019-06-22

本文共 1725 字,大约阅读时间需要 5 分钟。

题目

描述

小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 输入输出。

限制与约定

n≤10^6a_i≤10^6

时间限制:1s

空间限制:256MB

这货可能没用过自动缩进吧

题解(参考官方题解)

自己思考过程

第一步暴力枚举,发现没有啥规律,不满足二分性

第二步尝试暴力做,发现对每个数字可以优化的最大 tab 上限就是 \sqrt{a_i}

第三步看题解

第四步 看完来自己写一个题解

来解解吧

考虑 \Theta(n \log n) 的算法

不难发现,自己想根本想不出

那就考虑一下优化吧

第一步 sort+unique

这样对于相同的数字操作就不会重复了

然后考虑一下计数方式

假如我们用 ans[k] 记录 x=k 的答案

那么每次 k>\sqrt{a_i} 的时候,我们都需要在 ans[k] 上加一个 a_i ,每个数字实际操作仍为 \Theta(n)

反过来一下,我们在赋初值时使每一个 ans[k] 都为 \sum_{i=1}^{n}a_i

对于 k>\sqrt{a_i} ,我们每次都更新答案,减去可以减少的空格数

对于 k\le \sqrt{a_i} ,我们则不做操作

这样就目前来看操作复杂度为 \Theta (n\sqrt{a_i})

考虑枚举过程

容易发现,对于排序过后相邻的 a_i,a_{i+1} ,易有 \lfloor\frac{a_i}{k}\rfloor=\lfloor\frac{a_{i-1}}{k}\rfloor=t

a_i 的个数为 c_i ,那么则有

若对于 i\in [l,r] ,有 \lfloor\frac{a_i}{k}\rfloor=t ,那么 ans[k]-=(\sum_{i=l}^{r}c_i)\cdot t \cdot(k-1)

可用前缀和快速处理

找出最大的 a_i ,令其为 A

那么每一次对 k 枚举 t 时,t 上限就是 \lfloor \frac{A}{k} \rfloor

所以我们选择枚举 k\in [1,n] ,再枚举 t ,每一次枚举 t 都是 \lfloor \frac{A}{k} \rfloor

所以总的复杂度就是 \sum_{k=1}^{n} \lfloor \frac{A}{k} \rfloor=\Theta(n\log n)

可以说是非常优秀了

附上代码

#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);}复制代码

转载于:https://juejin.im/post/5a912ee3f265da4e8d43247f

你可能感兴趣的文章
VMware克隆虚拟机后网络不能正常使用的解决方法
查看>>
android平台TextView使用ImageSpan画廊GIF图像
查看>>
Android开发之ListView-SimpleAdapter的使用
查看>>
App.config提示错误“配置系统未能初始化”
查看>>
Angular - - ngChange、ngChecked、ngClick、ngDblclick
查看>>
JAVA学习第五十九课 — 网络编程概述
查看>>
远程共享文件夹
查看>>
convert2utf8withbom
查看>>
Codeforces Round #336 (Div. 2)A. Saitama Destroys Hotel 水题
查看>>
poj2752 Seek the Name, Seek the Fame(next数组的运用)
查看>>
pgpgin|pgpgout|pswpin|pswpout意义与差异
查看>>
全排列(递归与非递归实现)
查看>>
[转] C/C++中printf和C++中cout的输出格式
查看>>
swift 如何实现点击view后显示灰色背景
查看>>
【Android】3.9 覆盖物功能
查看>>
Plus One
查看>>
Git -- 创建版本库
查看>>
myeclipse 怎么安装与激活
查看>>
Atitit.异步编程的发展历史 1.1. TAP & async/await
查看>>
RTP timestamp与帧率及时钟频率的关系
查看>>