概述
题目大意:给n和m,求有多少方案,使得重新排列n的数位后能整除m,不能有前导0
(n<01e18,m<0100)
思路:数位dp
从高位到低位进行dp,dp[s][k]表示当前集合s(现在选了那几个数)模m后的余数为k的方案数
时间复杂度O(18*(2^18))
核心转移方程,dp[now][(上次余数*10+a[i])%m] += dp[pre][上次余数]
不过这样算会算重复,比如n=111,可以从101,011,110转移过来,所以最后要除以num[i]! (i出现的次数,i从1到9)
因为要排除前导0的情况,我先算最高位的方案数,这样就把0的方案排除了,比如n=210,最高位我算2,1就好了
注意:我们并不关心集合(状态s)的每个1是第几位,我们只关心上个状态的方案数,比如now = 101,我们只要看001和100的方案数,加上的1就现在处理的a[j](默认放在末尾)
(感觉这样写好理解一点,看了网上代码a[i]==0时候要contine不是很理解QAQ)
(语文不好,表达有点混乱。。。)
#include <iostream>
#include <stdio.h>
#include <cstdio>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <cstring>
#include <math.h>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define IOS ios::sync_with_stdio(false)
#define INF 0x7f7f7f7f7f7f7f7f
#define inf 0x7f7f7f7f
#define pb push_back
#define int long long
const int N=3e5+10;
int n,m,k;
int num[10];
int dp[(1<<18)][105];
vector<int > v;
void solve()
{
n = v.size();
for(int i=0 ;i<v.size() ;i++)//先算第一位,避免算到前导0
{
if(v[i])//非0
{
dp[1<<i][v[i]%m]=1;
}
}
for(int i=0 ;i<(1<<n) ;i++)//state
{
for(int j=0 ;j<v.size() ;j++)//枚举选哪个
{
if( i&(1<<j) )
for(int k=0 ;k<m ;k++)
{
dp[i][(k*10+v[j])%m] += dp[i^(1<<j)][k];//转移后 - j = 转移前
}
}
}
_for(i,0,9)
{
_for(j,1,num[i])
{
dp[(1<<n)-1][0] /= j;
}
}
cout<<dp[(1<<n)-1][0]<<endl;
}
signed main()
{
// freopen("data.txt","r",stdin);
cin>>n>>m;
while( n )
{
v.pb(n%10);
num[n%10]++;
n /= 10;
}
solve();
}
最后
以上就是复杂歌曲为你收集整理的codeforces401D Roman and Numbers(数位、状压dp)的全部内容,希望文章能够帮你解决codeforces401D Roman and Numbers(数位、状压dp)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复