• uoj140 【UER #4】被粉碎的数字


    题目

    看起来就像是数位( m dp)

    不妨从竖式乘法的角度来考虑这个问题

    为了方便处理进位,我们得从低位向高位填数

    (dp[i][0/1][j][p][t])表示填到了第(i)位,卡不卡上界,(f(x)=j)(f(k imes x)=p)(不计算最高位),需要向最高位进(t)(x)有多少个

    这里的卡上界比较奇怪,如果这一位上填的数大于(R)这一位上的数,那么就一定卡了上界,如果小于这一位上的数,那么就一定不卡上界,如果和原来的数相等,那么就继续之前的状态

    所以这个(0/1)就表示后面的(i)为和(R)(i)位的大小关系,如果填的数大于(R)(i)为,那么这个状态就是(1);否则就是(0)

    至于转移,就比较简单,我们枚举这一位上填什么数(y),那么对于(x),数位和增加了(y),对于(k imes x),这一位上直接来一个乘法是(k imes y),还有之前的进位(t),于是就是((k imes y+t)\% 10),新的进位就是((k imes y+t)/10)

    但是这样我们填到(R)的最高位之后可能还有一些进位没有处理,于是再往前多处理(3)位把进位处理完

    最后的答案就是(sum_{i}dp[lg_{R}][0][i][i][0]),即(x)的数位和等于(f(k imes x))的情况,但是这样多了一维非常难受,考虑的最后只要求(j=p),所以我们直接把(j-p)看成一维状态就好了

    代码

    #include<bits/stdc++.h>
    #define re register
    #define LL long long
    LL dp[25][2][1000][500];
    int m,w,a[25],M=250;LL n;
    inline void split(LL x) {
    	while(x) a[++w]=(x%10),x/=10;
    }
    int main() {
    	scanf("%lld%d",&n,&m);
    	dp[0][0][0][M]=1;
    	split(n);
    	for(re int i=0;i<w+3;i++)
    		for(re int j=0;j<2;++j)
    			for(re int k=0;k<1000;++k)
    				for(re int p=M-2*i*9;p<=M+2*i*9;++p) {
    					if(!dp[i][j][k][p]) continue;
    					for(re int t=0;t<10;++t)
    						dp[i+1][t==a[i+1]?j:t>a[i+1]][(k+t*m)/10][p+t-(k+t*m)%10]+=dp[i][j][k][p];
    				} 
    	printf("%lld
    ",dp[w+3][0][0][M]-1);
    	return 0;
    }
    
  • 相关阅读:
    js的浅拷贝与深拷贝
    用Nodejs连接MySQL(原文链接)
    HTML5交互性图表库
    GitHub Desktop离线安装包
    docker--Dockerfile--sonarqube
    docker --Nexus仓库
    docker --Dockerfile--一些语法
    zookeeper 四字命令
    docker --swarm创建一个集群
    docker --swarm启动2375端口监听
  • 原文地址:https://www.cnblogs.com/asuldb/p/11455315.html
Copyright © 2020-2023  润新知