Codeforces Round 961 (Div. 2) C. Squaring
首先,这道题目我费劲千辛万苦还是超时了,如果有大佬能进一步改进我的代码让它能过那就感激不尽了
先来看题目:
题目的大致意思就是:
对于一个给定的数组,你可以做如下操作:将某一个数变成它的平方,直到整个数组变成非递减的数组,求最小的操作次数
题目的要求非常简单,理解起来也很简单,很自然就能想到遍历一次,遇到比前一个小的就循环做平方直到比前一个大,但是用屁股也能想到这题不会这么简单就能做完,对一个数不断做平方的增长速度是非常快的,哪怕用double存都会超出范围,甚至都不需要几次操作就爆炸了,而且时间上也肯定过不了,那么我绞尽脑汁想出来了这么一个办法(尽管最后还是超时)(以下都是我当时的具体思考过程,有一些可能并不一定准确)
首先,应该要把整个数组都遍历一遍,因为你要找出所有不符合非递减的数,所以至少应该要是O(n) ,那么怎么尽量把时间控制在O(n)还要能够避免计算过大的数字呢
我们来看:
如果我对某一个数x,进行了5次平方操作,最后的结果应该是,pow(5,pow(2,5)),那么如果我第二个数是一个比x大的数,经过相同次数的操作,一定会比它更大,那如果第二个数比x小呢,我们可以先进行a次平方操作,让它比x更大,然后再进行同样多次的平方操作就肯定会比前一个数大了,欸,这样我们就避免了所有结果会大于1e6的平方计算,所有数的计算次数也都只会小于5(对2进行5次平方操作的结果是4.294967e9,可以看到增长的非常快),这样时间复杂度就控制在O(5n)以内了,这样我只要记录每个数的原始值,和它最后经过了几次操作,这样遍历到下一个数的时候,只要先让下一个数增加到大于前一个数的原始值,再加上上一个数经过的操作数,就得到了这个数需要几次平方操作
解释以下为什么这样能得到最小的操作次数:通过对原始值进行比较,如a<b,那么对a,b进行同样次数的平方操作后得到A,B显然也是A<B,所以我要先经过x次平方操作让a>b,这里的x是通过循环得到的最小值,然后加上同样多的次数就可以让A>B了,如果次数可以减一,那么就意味着x-1次就可以让a<b。
我原以为通过这种方式避免大数的出现,还能将时间复杂度控制在O(n)已经能过了,谁知道还是过不了(而且这个代码应该是能被hack的,不知道大家能不能看出来,因为没有实际计算平方操作后的值,所以其实有点难判断第二个数有没有大于前一个数平方后的值,我最初是写了一个循环来计算平方后的值,(超出1e6的就break,因为题目规定元素不会超过1e6)但是超时了之后我又改了一下,这次是面向结果改的,没啥参考价值,而且百分百能被hack,更何况还是超时的)不知道有没有大佬可以帮忙再改一改,从这种思路出发有没有可能写出这道题目
#include<bits/stdc++.h>
using namespace std;
int fun(int a)
{
return a * a;
}
int main()
{
int m = 0;
cin >> m;
while (m--)
{
int n=0;
cin >> n;
vector<int>arr(n);
for (int i = 0; i < n; i++)
{
cin >> arr[i];
}
int count1=0;
int num1 = arr[0];
int sum = 0;
int num2=arr[0];
bool flag = true;
bool flag2 = false;
for (int i = 1; i < n; i++)
{
int count2 = 0;
if (arr[i] <arr[i-1] || arr[i] <= fun(arr[i - 1]))
{
if (arr[i] == 1&&arr[i-1]!=1)
{
cout << -1 << endl;
flag = false;
}
else
{
int temp = arr[i];
while (arr[i] < num1)
{
arr[i] *= arr[i];
count2++;
}
if (temp <= num1)
{
sum += count1 + count2;
count1 += count2;
}
else if (temp >=fun(num1) && temp<fun(fun(num1)))
{
sum =sum+ count1 + count2 - 1;
count1 = count1 + count2 -1;
}
num1 = temp;
}
}
else
{
num1 = arr[i];
count1 = 0;
}
}
if(flag)
cout << sum << endl;
}
}