HDU X mod f(x)(题解注释)
X mod f(x)
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2792 Accepted Submission(s): 1101
Problem Description
Here is a function f(x):
int f ( int x ) {
if ( x == 0 ) return 0;
return f ( x / 10 ) + x % 10;
}
Now, you want to know, in a given interval [A, B] (1 <= A <= B <= 109), how many integer x that mod f(x) equal to 0.
Input
The first line has an integer T (1 <= T <= 50), indicate the number of test cases.
Each test case has two integers A, B.
Each test case has two integers A, B.
Output
For each test case, output only one line containing the case number and an integer indicated the number of x.
Sample Input
2
1 10
11 20
Sample Output
Case 1: 10
Case 2: 3
Author
WHU
Source
/*题意:计算区间内一个数字各位之和能整除该数字的个数 思路:分别计算出[1, b]中符合条件的个数和[1, a-1]中符合条件的个数。 d[l][i][j][k]表示前l位和为i模j的结果为k的数的个数,那么就有方程 d[l+1][i+x][j][(k*10+x)%j] += d[l][i][j][k] 预处理出d[l][i][j][k],然后再逐位统计即可。*/ #include <stdio.h> #include <string.h> #include <algorithm> #include <iostream> using namespace std; int bit[10]; int dp[10][82][82][82]; //d[l][i][j][k]表示前l位和为i模j的结果为k的数的个数 void set()//打表预处理出来你需要的数据 { int i,j,k,l,x; for(i = 1; i<=81; i++) dp[0][0][i][0] = 1; for(l = 0; l<9; l++)//枚举的是前l位 for(i = 0; i<=l*9; i++)//枚举的是当前和,最大和是l*9 for(j = 1; j<=81; j++)//不可能比81还大,总共才九位数,总和最大就是81,j>81的话得到的就是自己了 for(k = 0; k<j; k++) for(x = 0; x<=9; x++)//枚举的是当前位上的数 dp[l+1][i+x][j][(k*10+x)%j] += dp[l][i][j][k]; } //这个(k*10+x)%j是什么意思 //这个状态是前一个状态,位数比等号左边的少一位 //为什么要用k*10+x来模j呐 //因为吧,原来求的是前l位的和, //现在求得是l+1位的和了,以前的位数 //都向左移动了一位 int solve(int n) { if(!n) return 0; int ans,i,j,k,len; int sum,tem1,tem2,s,bit[10],r; len = sum = ans = 0; tem1 = tem2 = n; s = 1; while(tem1)//求每位数之和 { bit[++len]=tem1%10; tem1/=10; sum+=bit[len];//每位数之和 } if(n%sum==0)//本身要先看是否整除 ans++; for(i=1;i<=len;i++)//前i位 { sum-=bit[i];//将该位清0 tem2/=10;//现在个数是没有个位的 s*=10; tem1=tem2*s;//现在这个数个位上的数是零 for(j=0;j<bit[i];j++)//枚举该位的状况(就是遍历这个位上的数) { for(k=sum+j;k<=sum+j+9*(i-1);k++) //该位与更高位的和,而比该位低的和择优9*(i-1)种 {//9*(i-1)因为你枚举每多一位枚举的数字就会多出来9个 if(!k)//和为0的状况不符合 continue; r=tem1%k;//这里是要保证你枚举到的前i位再加上没枚举到那些位加起来不会超过原来的数 if(r) r=k-r;//余数大于0,那么k-r得到的数肯定能被k整除 ans+=dp[i-1][k-sum-j][k][r];//加上个数 } tem1+=s/10;//标记现在算到哪里,例如1234,一开始t是1230,然后1231,1232,1233,1234,接下来1200,就是1210,1220,1230 } } return ans; } int main() { //freopen("in.txt","r",stdin); int T,l,r,cas = 1; set();//先打表,半打表,将前l位,位数之和是i,并且模上j之后得到k的个数有多少 scanf("%d",&T); while(T--) { scanf("%d%d",&l,&r); printf("Case %d: %d\n",cas++,solve(r)-solve(l-1)); } return 0; }
自己又写了一遍,虽然都差不多,但是自己写一遍理清了思路
#include <stdio.h> #include <string.h> #include <algorithm> #include <iostream> #define N 10 #define M 82 using namespace std; int dp[N][M][M][M],g[N];//dp[l][i][j][k]表示前l位的和为i 模上j得数是k的数有多少个 void inti() { for(int i=1;i<=81;i++) dp[0][0][i][0]=1; //cout<<"ok"<<endl; for(int l=0;l<9;l++)//枚举的前l位 for(int i=0;i<=l*9;i++)//枚举的前l位的和 for(int j=1;j<=81;j++)//枚举的是你要模的那个数 for(int k=0;k<j;k++)//枚举的是模完的结果 for(int x=0;x<10;x++)//枚举的第l+1位 dp[l+1][i+x][j][(k*10+x)%j]+=dp[l][i][j][k]; //cout<<"ok"<<endl; //cout<<"ok"<<endl; } int solve(int n) { if(!n) return 0; int s,tem1,tem2,sum=0,r; tem1=tem2=n; s=1; int len=0; while(tem1) { g[++len]=tem1%10; tem1/=10; sum+=g[len]; }//分离各位,并且求出来和 int cur=0; if(n%sum==0) cur++; for(int i=1;i<=len;i++)//模拟的是前i位 { sum-=g[i];//先把这一位清零 tem2/=10; s*=10; tem1=s*tem2; for(int j=0;j<g[i];j++)//枚举的是这个位上的数 { for(int k=sum+j;k<=sum+j+9*(i-1);k++)//模拟的是你要模的那个数 { if(!k) continue;//如果k==0不符合条件 r=tem1%k; if(r) r=k-r; cur+=dp[i-1][k-sum-j][k][r]; //cout<<"cur="<<cur<<endl; //cout<<"dp[i-1][k-sum-j][k][r]="<<dp[i-1][k-sum-j][k][r]<<endl; } tem1+=s/10; } } return cur; } int main() { //freopen("in.txt","r",stdin); //cout<<"ok"<<endl; inti(); int t,l,r; scanf("%d",&t); //cout<<t<<endl; for(int i=1;i<=t;i++) { scanf("%d%d",&l,&r); //cout<<l<<" "<<r<<endl; printf("Case %d: %d\n",i,solve(r)-solve(l-1)); } return 0; }
我每天都在努力,只是想证明我是认真的活着.