uoj problem 21 缩进优化

题目:

小O是一个热爱短代码的选手。在缩代码方面,他是一位身经百战的老手。世界各地的OJ上,很多题的最短解答排行榜都有他的身影。这令他感到十分愉悦。
最近,他突然发现,很多时候自己的程序明明看起来比别人的更短,实际代码量却更长。这令他感到很费解。经过一番研究,原来是因为他每一行的缩进都全是由空格组成的,大量的空格让代码量随之增大。
现在设小O有一份 \(n\) 行的代码,第 \(i\) 行有 \(a_i\) 个空格作为缩进。
为解决这一问题,小O要给自己文本编辑器设定一个正整数的默认TAB宽度 \(x\),然后对于每一行,编辑器从头至尾不断把连续 \(x\) 个空格替换成一个TAB,直到剩余空格数不足 \(x\) 个。
最终缩进所占代码量为空格数与TAB数的和。请你帮小O选择一个合适的 \(x\),使得缩进所占代码量最小。

题解:

我们容易发现对于一个确定的\(x\),答案即为\(\sum_{i=1}^n[\frac{a_i}{x} + (a_i \mod x)]\)

因为有一个模运算,所以我们不好求值.

我们考虑去掉这个模运算.我们考虑计算每个转化可以减少多少代码量.

对于一个\(siz\)\(x\)的的TAB转化,可以减少\(x-1\)的代码量.

所以我们发现对于一个确定的x,可以减少的代码量为\((x-1)\sum_{i=1}^n[\frac{a_i}{x}]\)

然后我们考虑枚举,

首先我们枚举\(x\),然后枚举\([\frac{a_i}{x}]\)的值
然后可以利用桶统计在\([x*\frac{a_i}{x},x*(\frac{a_i}{x}+1))\)内的数的个数
更新答案即可.

容易发现这是\(O(nlogn)\)的.

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
#define rg register int
inline void read(int &x){
    x=0;char ch;bool flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 1000010;
int c[maxn];
int main(){
    int n;read(n);
    int m = 0;ll ans = 0;
    for(rg i=1,x;i<=n;++i){
	read(x);m = max(m,x);
	++ c[x];ans += x;
    }
    for(rg i=1;i<=m;++i) c[i] += c[i-1];
    ll del = 0,res = 0;
    for(rg x = 2;x <= m;++x){
	res = 0;
	for(rg y=1;y <= (m/x);++y){
	    res += 1LL*y*(c[min(x*(y+1)-1,m)] - c[x*y-1]);
	}
	del = max(del,res*(x-1));
    }
    printf("%lld\n",ans-del);
    return 0;
}


posted @ 2017-03-28 21:03  Sky_miner  阅读(250)  评论(0编辑  收藏  举报