1. 题目
报数(Number. pas/c/cpp)
题目描述
A营和B营之间不合已经是整个部队公开的了,所以他们没事就找对方麻烦。因为部队做集体训练,A营和B营很不幸分在了一组。。。所以又不安静了。。。
A营和B营都要报数,但是他们报数都很有意思,假设A营报的数为一个非递减的整数序列S1,S2,S3,……,Sn+1(Si<=Si+1)。而B营报的数为序列M1,M2,…,Mn为S的“M序列”,其中Mi=(Si+Si+1)/2。
例如,S=(1, 3, 3, 5),则M=(2, 3, 4)。
警官要求先报出M序列,B营当然不会放过这么好一个为难A营的机会,所以他们会一顿乱报,当然还是满足Si<=Si+1的。而你的任务就是帮助A营求出他们报数的S序列有多少种情况。
输入数据
第一行一个数n表示M的个数
第二到N+1行每行一个数Mi
输出数据
一个整数,表示A营有多少种报数方案
样例输入
3
2
5
9
样例输出
4
(存在如下四个报数方案满足要求:
2,2,8,10;
1,3,7,11;
0,4,6,12;
-1,5,5,13。)
数据范围
50%的数据n<=1000,mi<=20000
100%的数据2<=n<=100000,mi<=109.
2. 题目实质
数学数列部分习题的代码实现。
3. 算法
数论,不,这就是一个数学题。
首先,观察这个生成的数列,可以发现首项一确定,整个数列就可以确定了,所以我们只需要求出首项的范围。
令S序列的第一项为k,那么后面几项就可以写成关于k的多项式:
S1=k
S2=2*m1-k
S3=2*m2-2*m1+k
……
然后根据S序列的非递减性质,有S1<=S2<=S3<=….
所以有
k<=2*m1-k
2*m1-k<=2*m2-2*m1+k
……
可以得到n个关于k的不等式,而且都是有规律的,可以在O(n)的时间内解出:
x[i]+2*h[i-2]-2*h[i-1]( I 为偶)<= s[I] <= x[i]+2*h[i-2]-2*h[i-1] ( I 为奇)
由于k的值和S序列是一一对应的,所以k的取值的个数(b-a)就是满足要求的S序列的个数。
4. 注意事项
这个题代码很短,推导过程很长……
5. 程序代码
艾 FHAI (Pascal)
var
h,x:array[-3..100004]of longint;
r,l,n,i,j,t:longint;
begin
assign(input,'number.in');reset(input);
assign(output,'number.out');rewrite(output);
readln(n);
for i:=1 to n do
begin
readln(x[i]);
x[i]:=x[i]*2;
inc(h[i],x[i]);
inc(h[i],h[i-2]); //这样,每个 h 表示的是在他之前,奇偶性与他相同的各项的和。
end;
r:=maxlongint;l:=-maxlongint;
for i:=1 to n do
begin
t:=x[i]+2*h[i-2]-2*h[i-1];
if i mod 2=0 then
begin
if l<((-t) div 2)then l:=(-t) div 2; //表示各个偶数项和的 h 少加一个,所以 t<0 ,或者为了怕出错,可以直接取 abs(t)
end
else if r>(t div 2 )then r:=t div 2;
end; //求出 l ,r 即求出了首项的范围。
if r<l then write(0)
else write(r-l+1); //首项确定,整个数列即可确定。
close(input);close(output);
end.