http://codeforces.com/contest/1397/problem/B
题目大意:给定给一个长度为n的序列a,以下标0为开始.定义一个序列是牛逼的,当且仅当序列a里的元素满足ai=cii.其中c为某个任意整数,只要有一个c满足即可(即c不是指定的,存在一个c满足即可).现在可以任意重排整个序列,并用1的代价使序列里任意一个元素的值增加一或减少一,问把整个序列变成一个牛逼的序列最少需要多少的代价.
思路:
第一步题目给定了一个重排的操作,显然因为一个牛逼的序列一定是一个上升的序列(除了c=1的特殊情况,其他的都是严格上升的),所以一个比较直接的想法就是把整个序列按升序排序,再枚举所有可能的c,算出每个值的消耗,因为排序之后比不排序一定更好,所以算出来的结果一定正确.那么这里有一个问题:c的取值范围是多少.
从直觉上来说,这个题目的数据范围相当的大,c的取值也一定和a序列的和有关,而且可以猜到cc的取值范围是很有限的,而这个和可以到达1014.不过一个牛逼序列,同时是一个等比数列,在cc增大的过程中整个牛逼序列的和会上升的非常快,因此上界会很快的达到.那么对于单个的c的上界,不妨就按1e18作为INF,之后因为是一个等比数列,一共有n项,那么c的上界设置成是INF1n,因为整个牛逼序列的和不能过大,这样就可以保证了,当然这个上界并不准确,而且运算过程中可能会爆掉llll.所以在具体写的时候,是一步一步的计算出当前的和,如果已经超过了答案,那么就直接过掉,避免溢出.其次可以猜测这个复杂度是比较正常的,因为c并不会有多少个取值.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+7;
const ll INF = 1e18;
int a[N];
int main()
{
int n;scanf("%d",&n);ll res = INF,s = 0;
for(int i = 1;i <= n;++i) scanf("%d",&a[i]),s += a[i];
sort(a + 1,a + 1 + n);
int limit = pow(INF,1.0/n);
for(ll c = 1;c <= limit;++c)
{
ll loc = llabs(1 - a[1]),k = 1;
for(int i = 2;i <= n;++i)
{
k *= c;loc += llabs(a[i] - k);
if(loc > res) break;
}
res = min(res,loc);
}
printf("%lld",res);
return 0;
}