f[i] [j]为前缀和数组,a[i] [j]为原数组
f[i] [j] = f[i-1] [j] + f[i] [j-1] - f[i-1] [j-1] + a[i] [j]
算区间前缀和,画个图推公式
原数组a[i], 差分数组f[i] = f[i] - f[i-1], f[1] = a[1]
性质1:差分数组的前缀和序列为a, 即差分数组前缀和s[i], s[i] = a[i];
性质2:给原数组a区间在[l, r]各加d ,f[l] += d, f[r+1] -= d(减d则是f[l] -= d, f[r+1] += d), f其余不变---->当对原数组进行区间操作时,可以转化成对差分数组的单店操作
给定一个长度为 n 的数列 a1,a2,…,an每次可以选择一个区间 [l,r],使下标在这个区间内的数都加一或者都减一。
求至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列可能有多少种。
第一行输入正整数 n。
接下来 n 行,每行输入一个整数,第 i+1行的整数代表 ai。
第一行输出最少操作次数。
第二行输出最终能得到多少种结果。
0<n≤105
0≤ai<2147483648
4 1 1 2 2
1 2
已知差分数组性质2,每次可以令差分数组中的一个数+1,另一个数-1,目的是将差分数组b[2] - b[n]都变为0
先求出差分数组b,令psum为b中的正数和,nsum为b中的负数和的绝对值,之后可以先进行min(psum, nsum)次,进行相平,之后还差|psum - nsum|下相平,可以再把这些数字和b1或b[n-1]进行相平(b[1]与b[n+1]的值不影响结果),因此总操作次数为min(psum, nsum) + |psum - nsum|, 而总结果种类可以为0到|psum - nsum|任意一种,因此总结果种数为|psum - nsum| + 1
代码示例:
ll a[100005]; ll b[100005]; ll n, nsum, psum; ll ans; ll cnt; int main () { cin >> n; For(i, 1, n) cin >> a[i]; b[1] = a[1]; For(i, 2, n) { b[i] = a[i] - a[i-1]; if(b[i] < 0) nsum += b[i]; else psum += b[i]; } nsum *= -1; ll tm = psum - nsum; if(tm < 0) tm *= -1; ans += min(psum, nsum); ans += tm; cnt = tm + 1; cout << ans << endl << cnt << endl; return 0; }