增减序列
题目描述
题意解释
对区间 [ L , R ] [L,R] [L,R]内的序列都进行加1或者减1操作,使得 a 1 , a 2 , ⋯ , a n a_1,a_2,\cdots ,a_n a1,a2,⋯,an都是相同的。问题1:至少需要几步操作,可以使得 a 1 , a 2 , ⋯ , a n a_1,a_2,\cdots ,a_n a1,a2,⋯,an都是相同的。问题2:在这种最少操作步数的前提下,可以得到多少种序列。
分析
- b[1]=a[1]
- b[2]=a[2]-a[1]
- b[3]=a[3]-a[2]
- $\cdots \cdots \cdots $
- b[n]=a[n-1]-a[n]
由此可见,如果b[2]为0,那么a[2]=a[1];如果b[3]为0,那么a[3]=a[2], ⋯ \cdots ⋯;如果b[n]为0,那么a[n-1]=a[n]。因此,如果b[2],b[3],b[4], ⋯ \cdots ⋯,b[n]都为0,那么a[1]=a[2]=a[3]= ⋯ \cdots ⋯=a[n]。那么a序列就都是相同的了。也就是说,要想让a序列都相同的话,那么就必须让b[2],b[3], ⋯ \cdots ⋯,b[n]都为0,然后b[1]=a[1]可以任意选择。因此题目说了 0 ≤ a i ≤ 2147483648 0\leq a_i\leq2147483648 0≤ai≤2147483648。所以a[1]可以从 0 0 0到 2147483648 2147483648 2147483648范围内任意选择一个数,那么最终a序列就全都是这个数。
因此,题目问的问题就可以翻译为:
问题1:至少需要操作多少次,使得 b 2 b_2 b2~ b n b_n bn 都为0
问题2:在最少操作步数的前提下, b 1 b_1 b1 有多少种值
由差分意义可知,要想让 a L a_L aL~ a R a_R aR都加1或减1 ⟺ b L + = 1 , b R + 1 − = 1 \iff b_L+=1,b_{R+1}-=1 ⟺bL+=1,bR+1−=1。因为对于差分数组b来说,假设我们要对数列a的某个区间 [ L , R ] [L,R] [L,R]都进行加上常数c,那么就会写成 b [ L ] + = c , b [ R + 1 ] − = c b[L]+=c,b[R+1]-=c b[L]+=c,b[R+1]−=c,因此会用到 b R + 1 b_{R+1} bR+1,也就是说对数列 a 1 , a 2 , ⋯ , a n a_1,a_2,\cdots ,a_n a1,a2,⋯,an都要进行加1或减1操作,那么可以写成 b [ 1 ] + = 1 , b [ n + 1 ] − = 1 b[1]+=1,b[n+1]-=1 b[1]+=1,b[n+1]−=1,因此,差分数组b是从 b 1 b_1 b1~ b n + 1 b_{n+1} bn+1,共有 n + 1 n+1 n+1项,但是序列a它是有 n n n项。
题目对序列a的操作,相当于每次可以从差分数组b选出 b 1 , b 2 , ⋯ , b n + 1 b_1,b_2,\cdots ,b_{n+1} b1,b2,⋯,bn+1中的任意两个数,一个数加1,另一个数减1。目标是把 b 2 , b 3 , ⋯ , b n b_2,b_3,\cdots ,b_n b2,b3,⋯,bn全变成0,最终得到的数列a就是就 n n n个 b 1 b_1 b1构成的。
核心思路
首先要明确目标:让 b 2 , b 3 , ⋯ , b n b_2,b_3,\cdots ,b_n b2,b3,⋯,bn都为0,然后 b 1 b_1 b1随意。
从差分数组 b 1 , b 2 , ⋯ , b n b_1,b_2,\cdots ,b_n b1,b2,⋯,bn中任意选择两个数的方法可以分为四类:
- ( 1 ) (1) (1) 选择 b i b_i bi和 b j b_j bj,其中 2 ≤ i , j ≤ n 2\leq i,j\leq n 2≤i,j≤n。这种操作会改变 b 2 , b 3 , ⋯ , b n b_2,b_3,\cdots ,b_n b2,b3,⋯,bn中的任意两个数的值(假设选择了 b 5 b_5 b5和 b 10 b_{10} b10,那么就可以对 b 5 b_5 b5进行加1或减1操作,对 b 10 b_{10} b10进行减1或加1操作)。应该在保证 b i b_i bi和 b j b_j bj一正一负的前提下,尽量多地最好采取这种操作,这样会更接近目标。我们每一次选取 b i b_i bi和 b j b_j bj,这两个数一定是一正一负(最好是绝对值相同,比如 b i = 5 , b j = − 5 b_i=5,b_j=-5 bi=5,bj=−5)。为什么呢?因为我们想要让 b x ( 2 ≤ x ≤ n ) b_x(2\leq x\leq n) bx(2≤x≤n)为0而且是最少步数,那么假设我们选取的 b i b_i bi是正数, b j b_j bj是负数,那么让正数减1,负数+1,这样可以同时让正数向 0 0 0逼近,负数向 0 0 0逼近,是均衡的,会更块地让这两个数都接近0。假设我们选取的 b i b_i bi和 b j b_j bj都是正数(或者负数),那么让这两个正数同时减1,虽然是同时减1,但是这两个正数逼近0的速率不同,可能需要很多操作步数。因此,每次我们都要尽量让 b i b_i bi和 b j b_j bj同时选择正数和负数,这样才能更快并且步数少地接近0。如果没有正数和负数可以搭配了,比如剩下的 b x b_x bx都是正数或者都是负数,那么我们可以选出 b 1 b_1 b1或 b n + 1 b_{n+1} bn+1,因为这两个数都并不影响 b 2 , b 3 , ⋯ , b n b_2,b_3,\cdots ,b_n b2,b3,⋯,bn,这两个不管怎么操作都不会影响数的修改。
- $(2) $ 选择 b 1 b_1 b1和 b j b_j bj,其中 2 ≤ j ≤ n 2\leq j\leq n 2≤j≤n,那么就是让 b 1 b_1 b1加1(或减1), b 2 b_2 b2~ b n b_n bn中的某一个减1(或加1)。也就是说这种情况它只能让$b_2 ~ b_n 中 的 某 一 个 中的某一个 中的某一个b_x$加1(或减1),只能修改差分数组中的一个数而已。
-
( 3 ) (3) (3) 选择 b i b_i bi和 b n + 1 b_{n+1} bn+1,其中 2 ≤ i ≤ n 2\leq i\leq n 2≤i≤n,那么就是让 b n + 1 b_{n+1} bn+1加1(或减1), b 2 b_2 b2~ b n b_n bn中的某一个减1(或加1).也就是说这种情况它只能让 b 2 b_2 b2 ~ b n b_n bn中的某一个 b x b_x bx加1(或减1),只能修改差分数组中的一个数。
-
( 4 ) (4) (4) 选择 b 1 b_1 b1和 b n + 1 b_{n+1} bn+1。这种情况毫无意义。因为它并不会改变 b 2 , b 3 , ⋯ , b n b_2,b_3,\cdots ,b_n b2,b3,⋯,bn,这与我们的目标无关,相当于浪费了一次操作步数,一定步数最优解,因此这种情况不选,毫无意义。
因此,现在的核心想法就是如何运用上述的三种操作,可以更快用更少的操作取完成我们的目标。从直觉上看,我们应该多用第一种操作,因为第一种操作它一次可以改变差分数组的两个数,而第二、三种操作,它一次只能改变差分数组中的一个数。
设 b 2 , b 3 , ⋯ , b n b_2,b_3,\cdots ,b_n b2,b3,⋯,bn中正数总和为p,负数总和的绝对值为q。首先以正负数配对的方式尽量执行操作1,可执行 m i n ( p , q ) min(p,q) min(p,q)次,假设 p ≥ q p\geq q p≥q,也就是说负数个数比较少一些,正数个数比较多一些,那么就以负数个数为准,因为最少的负数个数决定了正负数配对的次数。因为正数个数比较多,所以此时差分数组中还剩下== ∣ p − q ∣ |p-q| ∣p−q∣个正数,而没有负数了,即还剩下 ∣ p − q ∣ |p-q| ∣p−q∣个未配对。那么就可以对剩下的这 ∣ p − q ∣ |p-q| ∣p−q∣个正数选择与 b 1 b_1 b1或 b n + 1 b_{n+1} bn+1配对,即进行操作2或操作3。因此,最少的操作步数就是: m i n ( p , q ) + ∣ p − q ∣ min(p,q)+|p-q| min(p,q)+∣p−q∣。由数学公式可以知道,其实 m i n ( p , q ) + ∣ p − q ∣ ⟺ m a x ( p , q ) min(p,q)+|p-q|\iff max(p,q) min(p,q)+∣p−q∣⟺max(p,q)==。
因为我们已经求出了最少步数,那么在最少步数的前提下,如何求出不同种的序列呢?
首先要明确,不同种序列是由 b 1 b_1 b1决定的。还剩下 ∣ p − q ∣ |p-q| ∣p−q∣个未配对,这剩下的 ∣ p − q ∣ |p-q| ∣p−q∣个数既可以和 b 1 b_1 b1配对,也可以和 b n + 1 b_{n+1} bn+1配对,那么 b 1 b_1 b1可以分配 0 , 1 , 2 , ⋯ , ∣ p − q ∣ 0,1,2,\cdots ,|p-q| 0,1,2,⋯,∣p−q∣个,与之对应的 b n + 1 b_{n+1} bn+1可以分配 ∣ p − q ∣ , ∣ p − q ∣ − 1 , ⋯ , 0 |p-q|,|p-q|-1,\cdots ,0 ∣p−q∣,∣p−q∣−1,⋯,0个,共有 ∣ p − q ∣ + 1 |p-q|+1 ∣p−q∣+1种情况。即进行操作2,由于操作2会改变 b 1 b_1 b1的值,因此,如果我们进行 ∣ p − q ∣ + 1 |p-q|+1 ∣p−q∣+1次操作2,就会修改 ∣ p − q ∣ + 1 |p-q|+1 ∣p−q∣+1次 b 1 b_1 b1,那么就会得到 ∣ p − q ∣ + 1 |p-q|+1 ∣p−q∣+1种 b 1 b_1 b1。所以在求出了最少步数的这种前提下,可以得到 ∣ p − q ∣ + 1 |p-q|+1 ∣p−q∣+1种不同的a序列。
代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100010;
typedef long long LL;
int a[N],b[N]; //a是数列a,b是差分数组
int main()
{
int n;
scanf("%d",&n);
//输入数列a
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
//构造差分数组b
for(int i=1;i<=n;i++)
b[i]=a[i]-a[i-1];
//p是b2,b3...,bn中正数总和 q是b2,b3...,bn中负数总和的绝对值
LL p=0,q=0;
for(int i=2;i<=n;i++)
{
if(b[i]>0) //统计差分数组中正数的总和
p+=b[i];
else //统计差分数组中负数绝对值的总和
q-=b[i];
}
//最少的操作步数就是min(p,q)+|p-q|,也就是max(p,q)
cout <<max(p,q)<<endl;
//不同种a序列 就是|p-q|+1
cout <<abs(p-q)+1<<endl;
return 0;
}