原题网址:http://www.lydsy.com/JudgeOnline/problem.php?id=1236
Time Limit: 1 Sec Memory Limit: 162 MB
Description
给你一个正整数N,依次把1到N写下来,在每两个数字之间交替的加上+,-号(注意是数字不是数),求这个表达式的值。例如,N=15时,表达式为1-2+3-4+5-6+7-8+9-1+0-1+1-1+2-1+3-1+4-1+5
Input
一个正整数N。
Output
表达式的值。
Sample Input
15
Sample Output
14
数据范围:
30%的数据中,N<=100。
100%的数据中,N<=10^15
这是道典型的数位dp题。
首先由于m位数(m≠1)的个数都是偶数个,所以第一个m位数开头都是负号。
对于奇数位数的数,2k和2k+1这两个数各位数和为1(前面的位相消,个位和为1),如:
-1+3-4
+1-3+5
对于偶数位数的数,会出现连续一段的同一位不仅数字相同,符号也相同。
对于一个m位数的n,对于小于m位的那些数因为每一种位数的数对应性质不同,而且都是满的,没有零头,可以拿出来单独处理。
剩下的那些m位数若是奇数位数,可由上面性质直接算出答案;若是偶数位数,则利用上面的性质自高位向低位统计答案,对于每一位,先算出整的部分,再算出零的部分当前位的贡献,就化归为规模更小的子问题了。
var
f:array[0..20,0..9] of int64;
c,d:array[0..20] of int64;
n,m,x,ans,s,k:int64;
i,j,l:longint;
begin
readln(n);
if n<=9 then
begin
ans:=0;
for i:=1 to n do
if i and 1=1
then inc(ans,i)
else dec(ans,i);
writeln(ans);
halt;
end;
ans:=5;
x:=n;m:=0;
while x>0 do
begin
inc(m);
d[m]:=x mod 10;
x:=x div 10;
end;
c[1]:=1;
for i:=2 to m do
c[i]:=c[i-1]*10;
for i:=0 to 9 do
f[1][i]:=i;
for i:=2 to m do //f[i][j]表示i位开头为j的数的数字和(方便起见,符号为+)
for j:=0 to 9 do
begin
f[i][j]:=c[i]*j;
for l:=0 to 9 do
dec(f[i][j],f[i-1][l]);
end;
for i:=2 to m-1 do //统计小于m位的答案
if i and 1=1
then inc(ans,c[i]*9 div 2)
else
begin
for j:=1 to 9 do
dec(ans,f[i][j]);
end;
if m and 1=1 //统计m位的答案
then
begin
inc(ans,(n-c[m]+1) div 2); //若是奇数位数直接算出答案
if n and 1=0 then
for i:=m downto 1 do
if i and 1=0
then inc(ans,d[i])
else dec(ans,d[i]);
end
else
begin
k:=-1;
for i:=m downto 1 do
begin
if i=m then s:=1 else s:=0;
for j:=s to d[i]-1 do
inc(ans,k*f[i][j]); //整的部分的答案
inc(ans,k*(n-d[i]*c[i]+1)*d[i]); //零的部分当前位的贡献
n:=n-d[i]*c[i]; //化归为子问题
k:=-k;
end;
end;
writeln(ans);
end.