题目
C. 平方
每次测试时间限制:2秒
每个测试的内存限制:256 MB
ikrpprpp 找到一个由整数组成的数组 a。他喜欢正义,所以他想要做到公平——也就是做到非减。为此,他可以对数组的索引 1 < i < n 执行正义行为,这会将 aż 替换为 a(位置 i 处的元素及其平方)。例如,如果 a = [2, 4, 3, 3, 5, 3] 并且 ikrpprpp 选择对 i = 4 执行正义行为,则 a 变为 [2, 4, 3, 9, 5, 3]。
最少需要多少次正义行为才能使数组不减?
输入
第一行包含一个整数 t (1 < t < 1000) 测试用例的数量。接下来是测试用例的描述。
对于每个测试用例,第一行包含一个整数 n a1, a2,…, an (1 ≤ a ≤ 10°)。数组a的大小。第二行包含 n (1 ≤ n ≤ 2105) 个整数
所有测试用例的 n 总和不超过 2 105。
输出
对于每个测试用例,打印使数组非递减所需的整数最小正义行为数。如果无法做到这一点,请打印 -1。
题目分析
初始思路
首先没办法之间从前往后跑一遍每次记录最大值,绝对会超,然后我一开始想的是预处理每一个arr[i]要翻多少翻能超过arr[i-1],或者要被前面那个arr[i-1]翻多少翻才能超越,然后把这些信息存储到新的数组中,后续利用该数组解决问题。
数学原理
传递性:
由于本题的数学特性
若
a
r
r
[
i
]
2
a
≥
a
r
r
[
i
−
1
]
2
b
则
b
每加上
x
,
a
至少也要加上
x
才能保持
该不等式继续成立
若arr[i]^{2^{a}}\geq arr[i-1]^{2^{b}}则b每加上x,a至少也要加上x才能保持\newline 该不等式继续成立
若arr[i]2a≥arr[i−1]2b则b每加上x,a至少也要加上x才能保持该不等式继续成立
因此本体每一个arr[i]对应的a是能够通过讨论arr[i]与arr[i-1]的关系以及讨论arr[i-1]的那个b来推算出来的,也就是说具有传递性。
P.S.我之前还有之后所说的所有“翻了多少翻”指的都是该不等式中a/b的值。
中有波折
奈何有一个细节处理 出了问题,一直没有用我的思想真正解决这道题
膜拜学习
后来看了tourist的代码,真是清晰简明啊,茅塞顿开,对于我原来的思路为何卡住同样有了理解,这个接下来再说。
成熟思路
首先找到第一个不是1的数,然后从该数字的下一个数字开始往后遍历,遍历途中如果遇到1就直接返回-1。
然后用last记录上一个数字翻了多少翻,cur计算该数字需要翻多少翻才能超过上一个数字,两个变量均保持为正数。然后考虑到该数字比上一个数字要大,可以在last的前提下不需要翻那么多次,也就利用到了之前所说的数学原理,让last减去k,k=上一个数字翻多少翻能超过该数字。
最终cur = cur +last;
解答
完整代码
premise code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define For for (ll i = 1; i <= n; i++)
#define rFor for (ll i = n; i > 0; i--)
#define rep(i, sta, end) for (ll i = sta; i <= end; i++)
#define rrep(i, end, sta) for (ll i = end; i >= sta; i--)
#define All(x) for (auto item : x)
key code
inline void solve() {
ll n;cin>>n;
vector<ll> arr(n+10);
For cin>>arr[i];
ll sta=1;
while(sta<=n&&arr[sta]==1){
sta++;
}
ll cur=0,last=0,ans=0;
rep(i,sta+1,n){
if(arr[i]==1){
cout<<-1<<endl;
return;
}
ll x= arr[i];
while(x<arr[i-1]){
x*=x;
cur++;
}
x= arr[i-1];
while(last>0&&x*x<=arr[i]){
x*=x;
last--;
}
//cout<<i-1<<"~"<<i<<" "<<cur<<" "<<last<<" "<<cur+last<<endl;
cur += last;
ans += cur;
last = cur;
cur=0;
}
cout<<ans<<endl;
}
算法分析
只需要O(n)