P4552 [Poetize6] IncDec Sequence - 洛谷
题目描述
给定一个长度为 n n n 的数列 a 1 , a 2 , ⋯ , a n {a_1,a_2,\cdots,a_n} a1,a2,⋯,an,每次可以选择一个区间 [ l , r ] [l,r] [l,r],使这个区间内的数都加 1 1 1 或者都减 1 1 1。
请问至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列有多少种。
输入格式
第一行一个正整数
n
n
n
接下来
n
n
n 行,每行一个整数,第 $i+1 $行的整数表示
a
i
a_i
ai。
输出格式
第一行输出最少操作次数
第二行输出最终能得到多少种结果
样例 #1
样例输入 #1
4
1
1
2
2
样例输出 #1
1
2
提示
对于 100 % 100\% 100% 的数据, n ≤ 100000 , 0 ≤ a i ≤ 2 31 n\le 100000, 0 \le a_i \le 2^{31} n≤100000,0≤ai≤231。
分析
这种类型的题感觉有很多。可以把它想象成是一堆矩阵再处理,这是这类题目的一种解决方式。因为深受acwing上之前一题求最大矩阵面积的做法影响,我刚开始是把每一个矩阵左边和右边第一个比他高的矩形求出来。这样是可以划定一个区域的,可以把这些矩阵范围排列一遍。说不定可以,但是题中要求方案数,这种做法太麻烦了。
做题千万不要想到一个办法就做到底。这是我做模拟赛时得出的深痛教训,我那次打了350行代码还错了。题解比我写的简单多了。
看了一下标签,是差分。
我一想也有道理:差分仅通过改变差分数组,可以改变一个区域内的值。但我还是想不出来,于是看了题解:
第一步
因为要让所有的数字相等,那这时的差分序列也就是 a[ i ] 0 0 0 …… 总之除了第一个全是 0 。那么我们的目的就是把差分数组中所有的数都变成 0(当然除了第一个)。
我们上面有提到:改变数组一个区间内的值,只要在差分序列左边 加减,右边减加就好了。那为了找到最小方案数,我们肯定希望有现成的对吧。就是前面负后面正的,这样就是所谓的一次修改。剩下的我们就只能一次一次加或减了。用 num_add 表示正数总和,num_subtra 表示负数总和,最小方案数就是 min(num_add, q_subtra) + abs(num_add - num_subtra)
也就是 max(num_add, num_subtra)
第二步,如何求方案数
为什么会有多种方案?是由于b[ 1 ] 的值造成的。因为我们之前求最小步数是不考虑b[ 1 ]的。我们再明确一下目的:把除b[ 1 ]外的数都变成 0。所以当配对完成之后,仅有几个不是 0 ,我们将这几个不是 0 的通过加减变成 0。那么此时b[ 1 ]可以加减也可以不加减,这就是方案数的求法,搜雷瓦gin集资!那么方案数就是 abs(num_add - num_subtra)。
#include<bits/stdc++.h>
using namespace std;
#define N 100010
long long ans1, ans2;
long long n, a[N], cf[N], bun_add = 0, num_subtra = 0;
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
cin >> a[i];
}
for(int i = 2; i <= n; i ++)
{
int c;
c = a[i] - a[i - 1];
if(c > 0)
bun_add += c;
else
num_subtra -= c;
}
ans1 = max(bun_add, num_subtra);
if(bun_add > num_subtra)
ans2 = bun_add - num_subtra + 1;
else
ans2 = num_subtra - bun_add + 1;
cout << ans1 << endl << ans2;
return 0;
}