Bzoj 1236 SPOJ1433 KPSUM

18 篇文章 0 订阅
1 篇文章 0 订阅

原题网址: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.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值