Loading

洛谷P4127

Description

给出两个数 \(a\)\(b\) ,求出 \([a,b]\) 中各位数字之和能整除原数的数的个数


Solution

\(f[i][j][k][q]\) 表示 枚举到第 \(i\) 位,当前数字为 \(j\) ,各个数位上的数字和为 \(k\),原数模 \(k\) 的余数为 \(q\)

可以发现,从 \(i - 1\) 位推到第 \(i\) 位并不好推,所以可以转变一下

用第 \(i\) 位推第 \(i + 1\)

显然有

\[f[i+1][s][j+s][(k+s\times 10^i)\mod x]=\sum_{t=0}^9f[i][t][j][k] \]

约束条件为 \(i\in [1,len]\)\(j\in [0,\min\{i\times 9,x\}]\)\(s\in[0,9]\)\(k\in[0,x)\)

其中 \(i\) 是枚举位数,\(s\) 是下一位上的数,\(t\) 是当前位上的数,\(x\) 是总数位和,\(k\) 是余数,\(j\) 是当前累计的数位和

然后对于答案的统计:

  • 对于位数小于当前位的,\(ans+=f_{i,10,x,0}\) \(−\) \(f_{i,0,x,0}\)
  • 对于位数相同的,计算小于最高位的 \(ans+=f_{num_i,s,x,0}\)
  • 然后对于当前不同的一位进行讨论即可,计算出后面所需要的余数,然后加和

\(num_i\)\(i\) 的最高位,\(x\) 是要求的位数和

答案就是所有位数和的 \(ans\) 之和, 同时要满足 \([l,r]\)


Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstring>
#define int long long

using namespace std;	

int A,B;
int f[20][11][172][172];
int highest[20],a[20][2];
int Mod,Pow[20];

inline int read(){
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
	while(ch>='0' &&ch<='9') s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
	return w*s;
}

inline int Min(int x,int y){return x<y?x:y;}
inline void F(int x,int y){highest[y]=0;while(x){a[++highest[y]][y]=x%10;x/=10;}}

inline void init(int x,int len){
	memset(f,0,sizeof f);
	for(register int i=0;i<=9;i++) f[1][i][i][i%x]++,f[1][10][i][i%x]++;
	for(register int i=1;i<len;i++)
		for(register int j=0;j<=Min(i*9,x);j++)
			for(register int k=0;k<x;k++){
				if(f[i][10][j][k])for(register int s=0;s<=9;s++){
				    	f[i+1][s][j+s][(k+s*Pow[i])%x]+=f[i][10][j][k];
				    	f[i+1][10][j+s][(k+s*Pow[i])%x]+=f[i][10][j][k];
				    }
			} 
	Mod=x;
}

inline int solve(int s,int x){
	int ans=0,num=a[highest[s]][s],now=num;
	for(register int i=1;i<highest[s];i++) ans+=f[i][10][x][0]-f[i][0][x][0];//处理最高位之前
	for(register int i=1;i<a[highest[s]][s];i++) ans+=f[highest[s]][i][x][0];//处理最高位 
	if(x<now) return ans;
	for(register int i=highest[s]-1;i;i--){
		int tot=(x-num*Pow[i]%x)%Mod;
		for(register int j=0;j<a[i][s];j++)ans+=f[i][j][x-now][tot];//操作最高位 
		now+=a[i][s];num=(num*10+a[i][s])%Mod;
		if(x<now) break;	
	}
	return ans;
}

signed main(){
	int ans=0;A=read();B=read();F(A,0),F(B+1,1);Pow[0]=1;
	for(register int i=1;i<=18;i++) Pow[i]=Pow[i-1]*10; 
	for(register int i=1;i<=highest[1]*9;i++){init(i,highest[1]);ans+=solve(1,i)-solve(0,i);}
	printf("%lld",ans);
	return 0;
}

posted @ 2020-12-01 21:42  KnightL  阅读(74)  评论(1编辑  收藏  举报