• JZOJ 5831. 【NOIP提高A组模拟2018.8.18】 number(数位DP)


    JZOJ 5831. 【NOIP提高A组模拟2018.8.18】 number

    题目

    Description

    给定正整数 n,m,问有多少个正整数满足:
    1、不含前导 0;
    2、是 m 的倍数;
    3、可以通过重排列各个数位得到 n。

    Input

    一行两个整数 n,m。

    Sample Input

    1 1

    Output

    一行一个整数表示答案对 998244353 取模的结果。

    Sample Output

    1

    Data Constraint

    对于 20%的数据,n<10^10。
    对于 50%的数据,n<10^16,m<=20。
    对于 100%的数据,n<10^20,m<=100。

    题解

    看看题目,第3个条件很好地说明了这道题目的算法—数位DP
    既然如此,我们想想如何设做,状态怎么设,需要维护哪些条件。不妨对每个条件来做出一定处理。
    条件1:DP第1位时从1至9,而其他的是从0至9即可;
    条件2:设一维状态表示对于m取余的结果;
    条件3:将每个数选择的个数的状态压缩成一个数来维护。
    于是,我们设 f [ i ] [ s ] [ j ] f[i][s][j] f[i][s][j]表示第 i i i位,选数的状态为 s s s,对于m取余的结果为 j j j的方案数。
    状态转移方程如下:
    f [ i + 1 ] [ s ′ ] [ [ ( j ∗ 10 + k ) m o d    m ] = ( f [ i + 1 ] [ s ′ ] [ ( j ∗ 10 + k ) m o d    m ] + f [ i ] [ s ] [ j ] ) f[i+1][s'][[(j*10+k)\mod m]=(f[i+1][s'][(j*10+k)\mod m]+f[i][s][j]) f[i+1][s][[(j10+k)modm]=(f[i+1][s][(j10+k)modm]+f[i][s][j])
    其中, k k k为每次枚举当前加入的数字, s ′ s' s为在 s s s的基础上添加 k k k压缩后在状态,注意时刻要保证当前 k k k的个数不能大于 n n n k k k的个数。
    最后的答案则为 f [ l e n ] [ S ] [ 0 ] f[len][S][0] f[len][S][0],其中 l e n len len n n n的数字位数, S S S n n n中数字出现的状态。
    因为时间过大,维护 q [ i ] [ s ] = 0 / 1 q[i][s]=0/1 q[i][s]=0/1表示 f [ i ] [ s ] [ 0   m − 1 ] f[i][s][0~m-1] f[i][s][0 m1]中是否至少有一位有值,因为我们记录的是方案数,所以如果都是 0 0 0时就可以跳过了。每次更新 f [ i + 1 ] [ s ′ ] [ [ ( j ∗ 10 + k ) m o d    m ] f[i+1][s'][[(j*10+k)\mod m] f[i+1][s][[(j10+k)modm]时把 q [ i + 1 ] [ s ′ ] = 1 q[i+1][s']=1 q[i+1][s]=1即可。
    同时,由于空间过大,要使用滚动DP。

    代码

    #include<cstdio> 
    #include<cstring>
    using namespace std;
    int a[15],b[15],g[15],p[60010][110],q[21][60010];
    long long f[2][60010][110];
    char z[21];
    int main()
    {
    	int m,i,j,k,l,s;
    	scanf("%s",z+1);
    	scanf("%d",&m);
    	int n=strlen(z+1);
    	for(i=1;i<=n;i++) a[z[i]-'0']++;
    	g[10]=1;
    	for(i=9;i>=0;i--) g[i]=g[i+1]*(a[i]+1);
    	memset(f,0,sizeof(f));
    	for(i=1;i<=9;i++) if(a[i]) f[1][g[i+1]][i%m]=1,q[1][g[i+1]]=1;
    	for(i=1;i<n;i++)
    	{
    		for(j=0;j<g[0];j++) if(q[i][j])
    		{
    			int t=j;
    			for(k=0;k<=9;k++)
    			{
    				b[k]=t/g[k+1];
    				t%=g[k+1];
    			}
    			for(k=0;k<=9;k++)
    			{
    				if(b[k]==a[k]) continue;
    				b[k]++;s=0;
    				for(l=0;l<=9;l++) s+=b[l]*g[l+1];
    				for(l=0;l<m;l++) if(f[i%2][j][l])
    				{
    					if(p[s][(l*10+k)%m]!=i+1) 
    					{
    						p[s][(l*10+k)%m]=i+1;
    						f[1-i%2][s][(l*10+k)%m]=f[i%2][j][l];
    						q[i+1][s]=1;
    					}
    					else f[1-i%2][s][(l*10+k)%m]+=f[i%2][j][l];
    					f[1-i%2][s][(l*10+k)%m]%=998244353;
    				}
    				b[k]--;
    			}
    		}
    	}
    	printf("%lld",f[n%2][g[0]-1][0]);
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    编程开发之--单例模式(2)
    编程开发之--单例模式(1)
    oracle 存储过程
    数据结构与算法之--最大公约数、最小公倍数
    编程开发之--Oracle数据库--存储过程使用动态参数绑定(3)
    软件开发之常用的工具
    Oracle PL/SQL学习之你需要知道的快捷键
    Linux下安装Tomcat服务器和部署Web应用
    如何在linux下安装tomcat服务器
    CentOS7 64位安装mysql教程
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910117.html
Copyright © 2020-2023  润新知