# 396 旋转函数
### **[396. 旋转函数](https://leetcode-cn.com/problems/rotate-function/)**
难度中等192
给定一个长度为 `n` 的整数数组 `nums` 。
假设 `arrk` 是数组 `nums` 顺时针旋转 `k` 个位置后的数组,我们定义 `nums` 的 **旋转函数** `F` 为:
- `F(k) = 0 * arrk[0] + 1 * arrk[1] + ... + (n - 1) * arrk[n - 1]`
返回 *`F(0), F(1), ..., F(n-1)`中的最大值* 。
生成的测试用例让答案符合 **32 位** 整数。
**示例 1:**
```
输入: nums = [4,3,2,6]
输出: 26
解释:
F(0) = (0 * 4) + (1 * 3) + (2 * 2) + (3 * 6) = 0 + 3 + 4 + 18 = 25
F(1) = (0 * 6) + (1 * 4) + (2 * 3) + (3 * 2) = 0 + 4 + 6 + 6 = 16
F(2) = (0 * 2) + (1 * 6) + (2 * 4) + (3 * 3) = 0 + 6 + 8 + 9 = 23
F(3) = (0 * 3) + (1 * 2) + (2 * 6) + (3 * 4) = 0 + 2 + 12 + 12 = 26
所以 F(0), F(1), F(2), F(3) 中的最大值是 F(3) = 26 。
```
**示例 2:**
```
输入: nums = [100]
输出: 0
```
**提示:**
- `n == nums.length`
- `1 <= n <= 105`
- `100 <= nums[i] <= 100`
通过次数34,611提交次数66,201
## 题目分析
$由题意,我们这里要求k使得f(k) = \quad \sum_{i = 0}^{n-1}(i*num_k[i])最大$
$因此我们可以展开该式有,$
$=(n-1)*num_k[0]+(n-2)*num_k[1]+······+(n-n)*num_k[n]$
$=n*(\sum_{i=0}^{n-1}(num_k[i]))-\sum_{i=0}^{n-1}((i+1)*num_k[i])$
$=n*(\sum_{i=0}^{n-1}(num_k[i]))-\sum_{i=0}^{n-1}(\sum_{j=0}^{i}num_k[j])$
$这里我们定义Sum(n) = \sum_{i=0}^{n-1}num_k[i]$
即为翻转k次所有项的和,其中由于无论如何旋转元素是不变的,因此等价于数组的总和
$接下来我们定义 presum_k(n) = \sum_{i=0}^{n-1}num_k[i] 为翻转k个数后的序列前缀和\quad其中有递推式 presum_k(n) = presum_k(n-1)+num_k[n]$也就是我们的前缀和
而这里由于需要我们也设定一个当旋转k后前缀和的总和函数 $Sum\_pre_k(n) = \sum_{i=0}^{n-1}presum(i+1)$
由此 我们再重新整理原式
$得 n*Sum(n)-Sum\_pre_k(n)$
因此这里我们原本的问题变成了求上式的最大值
由于我们之前分析过,无论如何旋转序列元素不变所以元素总和不变
因此 对于不同的k 仅有 $Sum\_pre_k(n)$在变换
所以原题就变成了求 $Sum\_pre_k$ 的最小值
一般的法子则是 枚举所有情况求出所有的和 由于 可以反转 n次 长度也为n
所以复杂度为O(n^2) 这里 $n<1e5$ 显然会爆
那根据刚才的我们发现 实际 $Sum\_pre_k(n)$是前缀和的前缀和所以复杂度仅有O(n)
但唯一问题是如何从旋转零次变换到旋转k次呢
$Sum\_pre_k(n) = presum_k(1)+presum_k(2)+·····+presum_k(n)$
$=num_k[0] +num_k[0]+num_k[1]+·······$
$Sum\_pre_{k+1}(n) = presum_{k+1}(1)+presum_{k+1}(2)+·····+presum_{k+1}(n)$
$=num_{k+1}[0] +num_{k+1}[0]+num_{k+1}[1]+·······$
$=num_{k}[1] +num_{k}[1]+num_{k}[2]+·······+num_{k}[0]+······num_k[n-1]$
$=Sum\_pre_{k}-n*num_k[0]+Sum(n)$
由此我们得到了递推公式 $Sum\_pre_{k+1}=Sum\_pre_{k}-n*num[0]+Sum(n)$
而最终解 $Min\_sum(n) = min(Min\_sum(n-1),Sum\_pre_{n})$
$f(k)_{min} = n*Sum(n)-Min\_sum(n)$
## 数据证明
最后抽象论证结束我们可以使用具体样例来进行检验
$$
nums = [4,3,2,6]
$$
$$
Sum(4) = 4+3+2+6 = 15\\15*4 = 60
$$
$$
presum_0(4) = 4*4+3*3+2*2+6*1 = 35\\ 60-35 = 25 \\Min\_sum(1) = 35
$$
$$
presum_1(4) = presum_0(4) -4*num[0] +Sum(4) = 35-4*4+15 = 34\\ 60-34 = 26 \\Min\_sum(2) = 34
$$
$$
presum_2(4) = presum_1(4) -4*num[1] +Sum(4) = 34-4*3+15 = 37\\ 60-37 = 23 \\Min\_sum(1) = 34
$$
$$
presum_3(4) = presum_2(4) -4*num[2] +Sum(4) = 37-4*2+15 = 44\\ 60-44 = 16 \\Min\_sum(1) = 34
$$
$$
presum_4(4) = presum_3(4) -4*num[3] +Sum(4) = 44-4*6+15 = 35\\ 60-35 = 25 \\Min\_sum(1) = 34
$$
$$
f(k)_{min} = 4*Sum(4)-Min\_sum(4) = 60 - 34 = 26
$$
# 数据规模分析
数组最大长度 1e5 数据最大1e2
根据公式 presum +=((size-i)*nums[i]);
因此数据最大到 1e5*1e2*1e5/2 因此int可能会爆
# 算法实现
```cpp
int maxRotateFunction(vector<int>& nums) {
long long sum = 0;
long long presum = 0;
long long min_sum = 0;
long long size = nums.size();
for(long long i = 0;i<size;i++){
sum+=nums[i];
presum +=((size-i)*nums[i]);
}
min_sum = presum;
for(int i = 0;i<size;i++){
presum-=(size*nums[i]);
presum+=sum;
min_sum = min(min_sum,presum);
}
return sum*size-min_sum;
}
```