测试链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3043
题目描述
给定一个长度为 (n) 的数列 ({a_i}),每次可以选择一个区间 ([l,r]),使这个区间内的数都加一或者都减一。
问至少需要多少次操作才能使数列中的所有数都一样。并求出在保证最少次数的前提下,最终得到的数列有多少种。
输入格式
第一行一个正整数 (n)。
接下来 (n) 行,每行一个整数,第 (i+1) 行的整数表示 (a_i)。
输出格式
第一行输出最少操作次数
第二行输出最终能得到多少种结果
数据范围
测试时间限制 (1000\, extrm{ms}),空间限制 (1\, extrm{GiB})。
对于 (100\%) 的数据,(nle 100000),(0le a_i< 2^{31})。
分析
注意到这里用的是区间修改,而最后询问的是次数和方法数。我们考虑差分。
啥?不知道差分?
差分就是将一个数列 ({a_i}) 相邻两项作差,得到一个新数列 (Delta{a_i})。
对于原数列 (a_i),构造差分数列 (Delta a_i),答案要求的就是 (forall iin [1,n) Delta{a_i}=0)。
而题目所要求的操作,就是给差分数列加上两项后,({a_i} (0le ile n)) 中两项,一项加一,另一项减一。
最小操作次数
注意到,若 (a_i (0<i<n)) 为正,则必须要减去至少 (a_i) 次。同理,如果 (a_i (0<i<n)) 为负,则必须至少加上 (|a_i|) 次
当两个“至少”同时达到时,就是最小操作次数。
即若 (p=sumlimits_{0< i< n\a_i>0}^n a_i),(q=sumlimits_{0< i< n\a_i<0}^n |a_i|),则最小操作次数就是 (max(p,q))。
方案数
注意到一加一减的操作不会影响 (sumlimits_{i=0}^n a_i),同时 (sumlimits_{i=1}^{n-1} a_i=0),则 (a_0 + a_n) 为定值。
又因为会有 (|p-q|) 个区间有多余的操作,将这两个操作分给两个数,一共有 (|p-q|+1) 种方案。
那就是答案。
Code
#include <cstdio>
#include <cctype>
using namespace std;
typedef long long ll;
inline int read()
{
int ch = getchar(), n = 0, t = 1;
while (isspace(ch)) { ch = getchar(); }
if (ch == '-') { t = -1, ch = getchar(); }
while (isdigit(ch)) { n = n * 10 + ch - '0', ch = getchar(); }
return n * t;
}
inline ll ab_max(ll a, ll b) { return (a > b)? a:b; }
inline ll ab_abs(ll a, ll b) { return (a > b)? a-b:b-a; }
int main()
{
int n = read(), pre = read(), now;
ll sum_pos = 0, sum_neg = 0;
for (int i = 1; i < n; i++)
{
now = read();
if (now > pre)
sum_pos += now - pre;
else if (now < pre)
sum_neg += pre - now;
pre = now;
}
printf("%lld
%lld
", ab_max(sum_pos, sum_neg), ab_abs(sum_pos, sum_neg) + 1);
return 0;
}