数位DP!!
题意:给一个很大的区间[x,y],(0 ≤ x ≤ y ≤ 1018).问:区间里面的数满足如下规则的有多少个?
规则:将数字放在天平上,天平能够平衡。天平的轴随意,力臂就是数字下标到天平轴的下标的距离。
思路:典型的数位DP,不过有一些技巧快速判断数字是不是满足规则。
定义:
区间分解:[X,Y]=[0,Y] -[0,X-1]
对于小于X的数:
sdig=sum(digit(i)){i=1..n,digit(i)表示数的第i个数字}
snum=sum(i*digit(i)){i=1..n}
每一次判断大数是不是平衡的,就看snum%sdig是不是0.
因为:如果将轴放在最高位的左边,那么天平将偏向一边,我们定义一个天平平衡度,就是snum.
如果天平平衡,那么snum=0
每次从高位向低位移动轴的时候,平衡度减小sdig,这个画画图就知道。于是判断能否整除就可以知道数字是不是平衡的。
View Code
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cmath> 6 using namespace std; 7 #define inf 1000000000 8 int d[25]; 9 __int64 dp[18][180][1600]; 10 //int dp[18][18*9+1][9*9*17+1]; 11 __int64 dfs(int i,int sdig,int snum,bool flag) 12 { 13 //准备枚举i位时,数字和sdig,数的位权和snum 14 if(i==0) 15 if(sdig==0 || snum%sdig==0)return 1; 16 else return 0; 17 if(!flag && dp[i-1][sdig][snum]!=-1) 18 return dp[i-1][sdig][snum]; 19 int limit; 20 __int64 sum; 21 if(flag)limit=d[i]; 22 else limit=9; 23 sum=0; 24 for(int s=0;s<=limit;s++) 25 sum+=dfs(i-1,sdig+s,snum+s*(i-1),flag&&s==limit); 26 if(!flag) dp[i-1][sdig][snum]=sum; 27 return sum; 28 } 29 int main() 30 { 31 int T; 32 __int64 A,B,SA,SB; 33 cin>>T; 34 memset(dp,-1,sizeof(dp)); 35 while(T--) 36 { 37 cin>>A>>B; 38 A--; 39 d[0]=0; 40 do{ d[++d[0]]=A%10; A/=10;}while(A); 41 SA=dfs(d[0],0,0,1); 42 d[0]=0; 43 do{ d[++d[0]]=B%10; B/=10;}while(B); 44 SB=dfs(d[0],0,0,1); 45 46 cout<<SB-SA<<endl; 47 } 48 return 0; 49 }