差分
差分是一种和前缀和相对的策略,可以当做是求和的逆运算。
定义为:
f ( n ) = { a i − a i − 1 , n > 2 a i , n = 1 f(n) = \begin{cases} a_i - a_{i-1}, &n>2\\ a_i, &n = 1 \end{cases} f(n)={
ai−ai−1,ai,n>2n=1
它具有一下这些性质:
- bi的前缀和是ai的值。
- 若在序列a的[l,r]区间增加一个数d,对b的表现为在b[l] +d,b[r+1]-d。(通过这个性质可以实现区间修改,单点查询)
具体:OIWIKI
参靠例题:1674. 使数组互补的最少操作次数
给你一个长度为 偶数 n 的整数数组 nums 和一个整数 limit 。每一次操作,你可以将 nums 中的任何整数替换为 1 到 limit 之间的另一个整数。
如果对于所有下标 i(下标从 0 开始),nums[i] + nums[n - 1 - i] 都等于同一个数,则数组 nums 是 互补的 。例如,数组 [1,2,3,4] 是互补的,因为对于所有下标 i ,nums[i] + nums[n - 1 - i] = 5 。
返回使数组 互补 的 最少 操作次数。
示例 1:
输入:nums = [1,2,4,3], limit = 4
输出:1
解释:经过 1 次操作,你可以将数组 nums 变成 [1,2,2,3](加粗元素是变更的数字):
nums[0] + nums[3] = 1 + 3 = 4.
nums[1] + nums[2] = 2 + 2 = 4.
nums[2] + nums[1] = 2 + 2 = 4.
nums[3] + nums[0] = 3 + 1 = 4.
对于每个 i ,nums[i] + nums[n-1-i] = 4 ,所以 nums 是互补的。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-moves-to-make-array-complementary
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
可以看出,对于每对数存在一个变化一次的区间[L,R],在这个区间外的即需要变化两次,在nums[i]+nums[n-i-1]这个位置的不需要变化。因为总的范围是[2,2limit],所以可以对每个数在区间上做修改,最后再扫描[2,2limit],获取变化到这个数的操作次数,不断维护最小值即可得到答案。
利用差分的做法如下:
const int MAX = 2e5+10;
int dat[MAX],N;
class Solution {
public:
int minMoves(vector<int>& nums, int limit) {
N = limit*2;
for(int i=0;i<=N;i+=1){
dat[i] = 0;
}
int len = nums.size();
for(int i = 0;i<nums.size()/2;i+=1){
int l = 1+min(nums[i],nums[len-i-1]);
int r = limit+max(nums[i],nums[len-i-1]);
int s = nums[i]+nums[len-i-1];
dat[l] -= 1;
dat[r+1] +=1;
dat[s] -= 1;
dat[s+1] += 1;
}
int res = len,now = len;//now是默认每对数都发生两次变化。
for(int i=2;i<=2*limit;i+=1){
now += dat[i];
res = min(res,now);
}
return res;
}
};
利用树状数组的解法如下:
const int MAX = 2e5+10;
int dat[MAX],N;
class BinaryIndexTree{
public:
void add(int p,int v){
while(p <= N){
dat[p] += v;
p += p&-p;
}
}
int sum(int p){
int res = 0;
while(p > 0){
res += dat[p];
p -= p&-p;
}
return res;
}
};
class Solution {
public:
int minMoves(vector<int>& nums, int limit) {
N = limit*2;
for(int i=0;i<=N;i+=1){
dat[i] = 0;
}
BinaryIndexTree bit;
int len = nums.size();
bit.add(2,len);
bit.add(2*limit+1,-len);
for(int i = 0;i<nums.size()/2;i+=1){
int l = 1+min(nums[i],nums[len-i-1]);
int r = limit+max(nums[i],nums[len-i-1]);
int s = nums[i]+nums[len-i-1];
bit.add(l,-1);
bit.add(r+1,+1);
bit.add(s,-1);
bit.add(s+1,+1);
}
int res = len;
for(int i = 2;i<=2*limit;i+=1){
res = min(res,bit.sum(i));
// cout << bit.sum(i) << endl;
}
return res;
}
};