• 【UOJ #140】被粉碎的数字【数位DP】


    description

    传送门

    题意简述:令\(f(x)\)\(x\)的各位数字之和,求:

    \[\sum_{i=1}^{R}[f(x)=f(kx)] \]

    其中\(R\le 10^{18}\)\(k< 1000\)

    solution

    遇到跟数字和相关的问题,自然想到数位\(DP\)

    为了处理\(kx\)进位的问题,我们从低位到高位考虑并维护目前\(kx\)向前一位进了多少:

    考虑\(dp\)状态\(dp[i][j][h][lim]\),表示已经考虑到了从低到高第\(i\)位,前\(i\)位中\(f(x)\)\(f(kx)\)的差为\(j\)(为避免负数问题,实际表示的是\(f(x)=f(kx)+j-200\)),目前\(kx\)向前\(1\)位进了\(h\)\(lim\)则是数位\(dp\)中经典的表示目前填的位置是否\(>R\),满足以上所有条件的\(x\)的个数

    数字之和最大不超过\(200\),因此\(j\)只用枚举到\(400\),因为\(k<1000\),所以进位不超过\(999\)

    转移就是直接枚举当前第\(i+1\)位选择的数\(x\),则有

    \[dp[i+1][j+x-(kx+h)\%10][(kx+j)/10][nlim]+=dp[i][j][h][lim] \]

    其中\(nlim\)满足:对于\(R\)的第\(i+1\)\(a[i+1]\),如果\(x>a[i+1]\),则\(nlim=1\),如果\(x<a[i+1]\),则\(nlim=0\),否则\(nlim=lim\)

    那么直接枚举转移即可,初始状态为\(dp[0][200][0][0]=1\)

    注意有可能填到\(R\)的最高位时还有一些进位没有处理,因此我们可以认为\(R\)多出了三位\(0\)来将进位处理完

    \(ans=\)

    \[dp[len][200][0][0]-1 \]

    \(-1\)是为了除去\(0\)\(len\)就是\(R\)的位数\(+3\)

    code

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    ll r,t,f[30][400][1010][2];
    ll a[50],len; 
    int main(){
    	scanf("%lld%lld",&r,&t);
    	ll p=r;
    	while(p){
    		a[++len]=p%10;
    		p/=10;
    	}	
    	len+=3;
    	f[0][200][0][0]=1;
    	for(int i=0;i<len;++i){
    		for(int j=0;j<=400;++j){
    			for(int k=0;k<=999;++k){
    				for(int lim=0;lim<=1;++lim){
    					if(!f[i][j][k][lim]) continue;
    					for(int x=0;x<10;++x){
    						int nr=(x*t+k)/10,nx=(x*t+k)%10; 
    						int nlim=lim;
    						if(x>a[i+1]) nlim=1;
    						if(x<a[i+1]) nlim=0;
    						f[i+1][j+x-nx][nr][nlim]+=f[i][j][k][lim];
    					}
    				}
    			}
    		}
    	}
    	printf("%lld\n",f[len][200][0][0]-1);
    	return 0;
    } 
    
  • 相关阅读:
    多态
    java8的十大新特性
    Floyd最短路径算法
    ES6(六)函数扩展
    ES6(五)数组扩展
    ES6(四)数值扩展
    ES6(一)解构赋值
    store封装
    ipad方案
    pyinstaller编译打包为pyd
  • 原文地址:https://www.cnblogs.com/tqxboomzero/p/14218460.html
Copyright © 2020-2023  润新知