『差分』BZOJ3043数列操作

P r o b l e m \mathrm{Problem} Problem

给定一个长度为n的数列{a1,a2…an},每次可以选择一个区间[l,r],使这个区间内的数都加一或者都减一。

问至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列有多少种。

S o l u t i o n \mathrm{Solution} Solution

由于是区间修改,我们考虑将区间修改转化为单点修改。

那么我们可以将问题转化为:

  • 我们另 b i = a i − a i − 1 , i ∈ [ 1 , n + 1 ] b_i=a_i-a_{i-1},i∈[1,n+1] bi=aiai1,i[1,n+1],得到一个长度 n + 1 n+1 n+1的差分序列。
    每一次操作可以选择 l l l r r r满足 l < r ll<r的前提下,对 b l b_l bl加上 x x x b r b_r br加上 − x -x x. ( x = ± 1 ) (x=±1) (x=±1)
  • 第一问:至少要进行多少次修改才能满足于所有 b 2 = b 3 = . . . = b n = 0 b_2=b_3=...=b_n=0 b2=b3=...=bn=0.
  • 第二问:在满足条件的情况下,有多少个不同的 b 1 . b_1. b1.

那么,对于第一问来说,另 s 1 = ∑ b i > 0 b i , s 2 = ∑ b i < 0 b i s_1=\sum_{b_i>0} b_i,s_2=\sum_{b_i<0} b_i s1=bi>0bi,s2=bi<0bi.那么若 s 1 > s 2 s_1>s_2 s1>s2,那么可以在每一个 s 2 s_2 s2加回0的时候顺带将 s 1 s_1 s1中的某一个位置减去 0 0 0,那么还有 s 2 − s 1 s_2-s_1 s2s1个位置需要操作,用差分数组的位置 b 1 b_1 b1 b n + 1 b_{n+1} bn+1进行操作即可。另一种情况同理,故答案为: max ⁡ ( s 1 , s 2 ) \max(s_1,s_2) max(s1,s2)

对于第二问来说,问你有多少个 b 1 b_1 b1,因为有 ∣ s 1 − s 2 ∣ |s_1-s_2| s1s2次机会可以操作,第一个位置最少操作0次,最多操作 ∣ s 1 − s 2 ∣ |s_1-s_2| s1s2次,故答案为: ∣ s 1 − s 2 ∣ + 1 |s_1-s_2| + 1 s1s2+1

代码如下:

#include  
#include 
#include 

#define int long long

using namespace std;
const int N = 200000;

int n, s1 = 0, s2 = 0; 
int a[N];

int read(void)
{
	int s = 0, w = 0; char c = getchar();
	while (c < '0' || c > '9') w |= c == '-', c = getchar();
	while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
	return w ? -s : s;
}

signed main(void)
{
	freopen("_.in","r",stdin);
	freopen("_.out","w",stdout);
	n = read();
	for (int i=1;i<=n;++i) a[i] = read();
	for (int i=2;i<=n;++i)
	{
		int t = a[i] - a[i-1];
		if (t >= 0) s1 += t;
		if (t < 0) s2 += -t;
	} 
	cout<<max(s1,s2)<<endl;
	if (s1 > s2) cout<<s1-s2+1<<endl;
	else cout<<s2-s1+1<<endl;
	return 0; 
}

你可能感兴趣的:(差分)