hihoCoder1033 交错和 数位DP

题目:交错和

链接:http://hihocoder.com/problemset/problem/1033#

题意:对于一个十进制整数x,令a0、a1、a2、...、an是x从高位到低位的数位,定义f(x)=a0-a1+a2-a3+...an,给出L、R、K,x在L到R之间,求所有满足:f(x)=k的x的和。(0 ≤ l ≤ r ≤ 10^18, |k| ≤ 100)

思路:

  L与R太大,连预处理的可能性都没有,很明显的数位DP。

  令dp[i][j]为精确的(有前导0)i 位,f(x)值为j 的x的和

  令dd[i][j]为精确的i 位,f(x)值为j 的x的个数。

  dp[1][i] = i (i从0-9)

  dd[1][i] = 1(i从0-9)

  当i为奇数时,最后一位前的符号位+,所以dp[i][j] = dp[i][j] + dp[i-1][j-u]*10 + u*dd[i-1][j-u] (u从0-9)

  dd[i][j]=dd[i][j] + dd[i-1][j-u] (u从0-9)

  当i为偶数时,将j-u改成j+u即可

  注意这里的dp求的和是有前导0的,比如3位数里098的f值为0-9+8也就是-1,所以dp[3][k]并不是0到1000里f值为k的数的和,因此我们要重新计算一个不含前导0的。

  令op[i][j]为i 位,f(x)值为j 的x的和

  令pp[i][j]为i 位,f(x)值为j 的x的个数。

  递推方法与dp一样,不同的是初始化,特别的令pp[1][0]=0,这样,等会递推出来的就是不包含前导0的,之后,为了方便,我们可以重新定义op[i][j]为前i 位,f(x)值为j 的x的和,pp也一样,进行下前缀和计算就可以了。

  解决完以上两步,这个问题便简单很多了,l到r,我们可以先算出0-r的,再减去0-l的,因此问题就转化成x属于0-l,f(x)值为k的x的和了,比如说3256,那么我们可以先算1000里有多少,1000-2000有多少,2000-3000有多少,再算3000-3100里有多少,3100-3200里有多少......1000里的直接就是op[3][k],1000到2000的,我们可以遍历u从0-9,现在就是1-u+?-?=k有多少了,那么?-?的值应该是k-1+u,也就是op[2][k-1+u],同时注意下细节就可以了。具体看代码。

  注:值得注意的是dd[0][0]=1,pp[0][0]=1

  

  1 #include<stdio.h>
  2 #include<iostream>
  3 using namespace std;
  4 #define Mod 1000000007
  5 typedef long long LL;
  6 LL dp[20][300]={0};
  7 LL dd[20][300]={0};
  8 LL op[20][300]={0};
  9 LL pp[20][300]={0};
 10 int getPos(int val)
 11 {
 12   return val+100;
 13 }
 14 void Init()
 15 {
 16   dd[0][100]=1;
 17   for(int i=0;i<=9;i++) dd[1][getPos(i)]=1,dp[1][getPos(i)]=i;
 18   for(int i=2;i<=18;i++)
 19   {
 20     if(i&1)
 21     {
 22       for(int j=0;j<10;j++)
 23       {
 24         for(int k=j;k<=200;k++)
 25         {
 26           dp[i][k]=(dp[i][k]+dp[i-1][k-j]*10%Mod+j*dd[i-1][k-j]%Mod)%Mod;
 27           dd[i][k]=(dd[i][k]+dd[i-1][k-j])%Mod;
 28         }
 29       }
 30     }
 31     else
 32     {
 33       for(int j=0;j<10;j++)
 34       {
 35         for(int k=0;k<=200-j;k++)
 36         {
 37           dp[i][k]=(dp[i][k]+dp[i-1][k+j]*10%Mod+j*dd[i-1][k+j]%Mod)%Mod;
 38           dd[i][k]=(dd[i][k]+dd[i-1][k+j])%Mod;
 39         }
 40       }
 41     }
 42   }
 43
 44   for(int i=0;i<=9;i++) pp[1][getPos(i)]=1,op[1][getPos(i)]=i;
 45   pp[1][getPos(0)]=0;
 46   for(int i=2;i<=18;i++)
 47   {
 48     if(i&1)
 49     {
 50       for(int j=0;j<10;j++)
 51       {
 52         for(int k=j;k<=200;k++)
 53         {
 54           op[i][k]=(op[i][k]+op[i-1][k-j]*10%Mod+j*pp[i-1][k-j]%Mod)%Mod;
 55           pp[i][k]=(pp[i][k]+pp[i-1][k-j])%Mod;
 56         }
 57       }
 58     }
 59     else
 60     {
 61       for(int j=0;j<10;j++)
 62       {
 63         for(int k=0;k<=200-j;k++)
 64         {
 65           op[i][k]=(op[i][k]+op[i-1][k+j]*10%Mod+j*pp[i-1][k+j]%Mod)%Mod;
 66           pp[i][k]=(pp[i][k]+pp[i-1][k+j])%Mod;
 67         }
 68       }
 69     }
 70   }
 71   pp[0][getPos(0)]=1;
 72   for(int i=1;i<=18;i++)
 73   {
 74     for(int j=0;j<=200;j++)
 75     {
 76       op[i][j]=(op[i][j]+op[i-1][j])%Mod;
 77       pp[i][j]=(pp[i][j]+pp[i-1][j])%Mod;
 78     }
 79   }
 80 }
 81
 82 LL solve(LL x,LL p)
 83 {
 84   if(x<0) return 0;
 85   LL tmp=x;
 86   int bt[20],bo=0;
 87   while(x)
 88   {
 89     bt[bo++]=x%10;
 90     x/=10;
 91   }
 92   LL io=1;
 93   for(int i=1;i<bo;i++)
 94     io=io*10;
 95   LL sum=0,k=0,kk=0;
 96   for(int i=bo-1;i>0;i--)
 97   {
 98     if((bo-i)&1)
 99     {
100       for(int j=0;j<bt[i];j++)
101       {
102         if(i==bo-1&&j==0)
103         {
104           sum=(sum+op[i][getPos(p)])%Mod;
105         }
106         else
107         {
108           for(int u=0;u<=9;u++)
109           {
110             sum=(sum+(kk*100%Mod+j*10+u)%Mod*io/10%Mod*dd[i-1][getPos(p+u-j-k)]%Mod+dp[i-1][getPos(p+u-j-k)])%Mod;
111           }
112         }
113       }
114       k+=bt[i];
115       kk=(kk*10%Mod+bt[i])%Mod;
116     }
117     else
118     {
119       for(int j=0;j<bt[i];j++)
120       {
121         sum=(sum+(kk*10%Mod+j)%Mod*io%Mod*dd[i][getPos(p+j-k)]%Mod+dp[i][getPos(p+j-k)])%Mod;
122       }
123       k-=bt[i];
124       kk=(kk*10%Mod+bt[i])%Mod;
125     }
126     io/=10;
127   }
128   if(bo&1)
129   {
130     for(int i=0;i<=bt[0];i++)
131       if(p-k==i)
132       {
133         sum=(sum+kk*10%Mod+i)%Mod;
134       }
135   }
136   else
137   {
138     for(int i=0;i<=bt[0];i++)
139       if(k-p==i) sum=(sum+kk*10%Mod+i)%Mod;
140   }
141   return sum;
142 }
143 int main()
144 {
145   Init();
146   LL l,r,k;
147   cin>>l>>r>>k;
148   cout<<(solve(r,k)-solve(l-1,k)+Mod)%Mod<<endl;
149   return 0;
150 }

AC代码

  

时间: 2024-10-06 05:48:59

hihoCoder1033 交错和 数位DP的相关文章

[hihocoder 1033]交错和 数位dp/记忆化搜索

#1033 : 交错和 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定一个数 x,设它十进制展从高位到低位上的数位依次是 a0,?a1,?...,?an?-?1,定义交错和函数: f(x)?=?a0?-?a1?+?a2?-?...?+?(?-?1)n?-?1an?-?1 例如: f(3214567)?=?3?-?2?+?1?-?4?+?5?-?6?+?7?=?4 给定 输入 输入数据仅一行包含三个整数,l,?r,?k(0?≤?l?≤?r?≤?1018,?|k|

hihoCoder #1033 : 交错和 [ 数位dp ]

传送门 #1033 : 交错和 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定一个数 x,设它十进制展从高位到低位上的数位依次是 a0, a1, ..., an - 1,定义交错和函数: f(x) = a0 - a1 + a2 - ... + ( - 1)n - 1an - 1 例如: f(3214567) = 3 - 2 + 1 - 4 + 5 - 6 + 7 = 4 给定 l, r, k,求在 [l, r] 区间中,所有 f(x) = k 的 x 的和,即

hihocoder1033(数位dp)

题目: 给定一个数 x,设它十进制展从高位到低位上的数位依次是 a0,?a1,?...,?an?-?1,定义交错和函数: f(x)?=?a0?-?a1?+?a2?-?...?+?(?-?1)n?-?1an?-?1 例如: f(3214567)?=?3?-?2?+?1?-?4?+?5?-?6?+?7?=?4 给定 输入 输入数据仅一行包含三个整数,l,?r,?k(0?≤?l?≤?r?≤?1018,?|k|?≤?100). 输出 输出一行一个整数表示结果,考虑到答案可能很大,输出结果模 109?+?

[数位dp] hihoCoder 1033 交错和

题意: 问你[l,r]区间内的所有满足各个位一加一减最后和是k的全有数的和. 思路: 数位dp dp[site][sum][p][k] 代表site位,和是sum,当前是加还是减,最后和是k的数的和以及个数 也就是存成结构体. 然后求的时候 ans.cnt=(ans.cnt+cur.cnt)%mod; ans.sum=(ans.sum+cur.sum+cur.cnt*tep)%mod; tep为i*当前位的位权. 代码: #include"cstdlib" #include"

数位dp/记忆化搜索

一.引例 #1033 : 交错和 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定一个数 x,设它十进制展从高位到低位上的数位依次是 a0, a1, ..., an - 1,定义交错和函数: f(x) = a0 - a1 + a2 - ... + ( - 1)n - 1an - 1 例如: f(3214567) = 3 - 2 + 1 - 4 + 5 - 6 + 7 = 4 给定 l, r, k,求在 [l, r] 区间中,所有 f(x) = k 的 x 的和,

[hdu 4933]Miaomiao&#39;s Function 数位DP+大数

Miaomiao's Function Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 79    Accepted Submission(s): 18 Problem Description Firstly , Miaomiao define two functions f(x) , g(x): (K is the smallest

51Nod 1009 数字1的个数 | 数位DP

题意: 小于等于n的所有数中1的出现次数 分析: 数位DP 预处理dp[i][j]存 从1~以j开头的i位数中有几个1,那么转移方程为: if(j == 1) dp[i][j] = dp[i-1][9]*2+pow(10,i-1);else dp[i][j] = dp[i-1][9]+dp[i][j-1]; 然后注意下对于每个询问统计的时候如果当前位为1需要额外加上他后面所有位数的个数,就是n%pow(10,i-1); 这样总复杂度log(n)*10 #include <bits/stdc++.

HDU 3555 Bomb (数位DP)

数位dp,主要用来解决统计满足某类特殊关系或有某些特点的区间内的数的个数,它是按位来进行计数统计的,可以保存子状态,速度较快.数位dp做多了后,套路基本上都差不多,关键把要保存的状态给抽象出来,保存下来. 简介: 顾名思义,所谓的数位DP就是按照数字的个,十,百,千--位数进行的DP.数位DP的题目有着非常明显的性质: 询问[l,r]的区间内,有多少的数字满足某个性质 做法根据前缀和的思想,求出[0,l-1]和[0,r]中满足性质的数的个数,然后相减即可. 算法核心: 关于数位DP,貌似写法还是

51nod1043(数位dp)

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1043 题意:中文题诶- 思路:数位dp 我们用dp[i][j]来存储长度为2*i且一半和为j的所有情况(包括前导0的情况),为了方便我们现在只讨论其一半的和的情况,因为如果包括前导0的话其两边的情况是一样的: 我们假设再长度为i-1的数字最前面加1位数字k,0<=k<=9(这位数字加在哪里并不影响答案,因为我们在计算i-1长度的时候已经计算了所有组合情况,