Codeforce 401D Roman and Numbers[数位DP+状态压缩]

给出数n和m,求n的所有排列中,模m得0的有多少个 n
(1?≤?n?<?1018) and
m (1?≤?m?≤?100).

暴力法我们直接枚举n的所有排列,显然18!超时。

考虑怎么dp

假设给了我们数n=23765

显然有

(237%m*10+6)%m=2376%m

(367%m*10+2)%m=3672

我们很自然的想到了

这样的状态转移

dp[i][k]

i代表取的数的状态

代表在取数状态为i的情况下模m为k的数有多少

比如

对于23765的356

取数状态为01011

dp方程就是

dp[i|(1<<j)][(k*10+n[j])%m](j位还没取)+=dp[i][k]

显然,dp[i|(1<<j)][(k*10+n[j])%m]这个状态是多次可达的,所以我们要+=

这里默认是把新数加到前一个状态所有组成的数的末尾,因为加到中间的情况可以由其他状态计算进去

比如

对356来说,现在要加入2

我们加入末尾3562,由上面的柿子算入

如果加到中间,比如成了3256,则这个状态是由325取6得到3256计算

最后,我们这样做是把所有的数字当做互不相同处理的,对相同的数,我们要除以所有重复的情况,即所有相同次数的阶乘的积

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const long long NN=1111;
char str[NN];
long long times[NN];
long long dp[(1<<18)][111];
int main(){
#ifndef ONLINE_JUDGE
	freopen("/home/rainto96/in.txt","r",stdin);
#endif

	long long m;scanf("%s%I64d",str,&m);
	long long len=strlen(str);
	long long all=(1<<len)-1;
	long long left=1;
	for(long long i=0;i<len;i++)
		left*=++times[str[i]-='0'];
        dp[0][0]=1;
	for(long long i=0;i<=all;i++){
		for(long long j=0;j<len;j++){
			if(!(i&(1<<j))){
				if(i || str[j]){
					for(long long k=0;k<=m-1;k++){
						dp[i|(1<<j)][(k*10+str[j])%m]+=dp[i][k];
					}
                                }
			}
		}
	}
	printf("%I64d\n",dp[all][0]/left);
}
时间: 2024-10-11 02:06:32

Codeforce 401D Roman and Numbers[数位DP+状态压缩]的相关文章

[Codefoces 401D]Roman and Numbers 数位dp

http://codeforces.com/problemset/problem/401/D 题目大意:给定一个数字n,将n的每一位数字重新排列,求在这些排列数之中可以被n整除的方法数. 解题思路: 暴力超时-- 大多数人的写法是进行位压缩,不过那样的话需要2^18*100 的空间,效率比较低,重复状态数较多,处理起来也不方便,这一题是给出了512M的空间.但是如果空间再小一倍,前者的方法就无能为力了. 发现有一种对于数位dp来说比较好的状态压缩方式,直接根据数码x出现的次数进行状态压缩.比如说

hdu 4352 数位dp + 状态压缩

XHXJ's LIS Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2265    Accepted Submission(s): 927 Problem Description #define xhxj (Xin Hang senior sister(学姐)) If you do not know xhxj, then careful

【HDU 4352】 XHXJ&#39;s LIS (数位DP+状态压缩+LIS)

XHXJ's LIS Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2422    Accepted Submission(s): 990 Problem Description #define xhxj (Xin Hang senior sister(学姐)) If you do not know xhxj, then careful

[数位dp+状态压缩] hdu 4352 XHXJ&#39;s LIS

题意: 给x.y.k,在[x,y] 范围内最长上升子序列长度是k的数有几个 思路: 模仿 LIS nlogn的想法,这里就只有10个数,进行状压 然后直接搜就好了不用二分 然后按位dp下去就ok了! 代码: #include"cstdlib" #include"cstdio" #include"cstring" #include"cmath" #include"queue" #include"al

CodeForces 55D Beautiful numbers 数位DP+数学

题意大概是,判断一个正整数区间内有多少个整数能被它自身的每一个非零的数字整除. 因为每一个位置上的整数集s = {0,1,2,3,4,5,6,7,8,9} lcm(s) = 2520 现在有一个整数t是由s中一个或者多个数字构成的,记为abcde,显然t = a*10^4+b*10^3+c*10^2+d*10^1+e 要使得t能被a,b,c,d,e整除,必然有t % lcm(a,b,c,d,e) = 0 因为a,b,c,d,e去重之后一定是s的一个子集,所以lcm(s)一定是lcm(a,b,c,

cf55dBeautiful numbers数位dp

想到 最小公倍数 其余的就好搞了 ,可是没想到 #include <cstdio> #include <cstring> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <cstdlib> #include <list> #include <

Balanced Numbers数位dp

三进制搞下, 0  表示没出现过,  第i位为1 表示 i出现了奇数次,  2表示i 出现了偶数次. #include <cstdio> #include <cstring> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <cstdlib> #includ

uva 10712 - Count the Numbers(数位dp)

题目链接:uva 10712 - Count the Numbers 题目大意:给出n,a,b:问说在a到b之间有多少个n. 解题思路:数位dp,dp[i][j][x][y]表示第i位为j的时候,x是否前面是相等的,y是否已经出现过n.对于n=0的情况要特殊处理前导0,写的非常乱,搓死. #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using na

poj 3340 Barbara Bennett&#39;s Wild Numbers(数位DP)

Barbara Bennett's Wild Numbers Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 3153   Accepted: 1143 Description A wild number is a string containing digits and question marks (like 36?1?8). A number X matches a wild number W if they hav