题目地址:cf#235_div2_D
题目大意:给你一个整数(相当于给你一个集合) 现在问你,这些整数的全排列(不能有前导零)当中,有多少被m整除
先直接暴力 18的阶乘 显然是会超时的。
可以过到第12组数据
#include<iostream>
#include<algorithm>
using namespace std;
long long n,m;
int pp[18];
int digit;
bool divided()
{
long long ans=0;
for(int i=0;i<digit;i++)
{
ans=ans*10+pp[i];
}
if(ans%m==0) return 1;
else return 0;
}
int main()
{
cin>>n>>m;
long long nn=n;
int index=0;
int p[18];
while(nn>0)
{
p[index++]=nn%10;
nn/=10;
}
digit=index;
for(int i=0;i<index;i++)
{
pp[i]=p[index-1-i];
}
sort(pp,pp+digit);
long long cnt=0;
do {
if(divided()&&pp[0]!=0)
cnt++;
} while (next_permutation(pp,pp+digit));
cout<<cnt<<endl;
}
然后就是dp的写法,参照了 yzc 的思路
实际上是枚举的最后一位的数码可能是什么,枚举完以后,拓扑序是集合元素的数量关系。
如果i==0 那么不能有前导零,所以数码不能为0
神奇只处在于 这样的贡献方式是补充不漏的。 具体原因还没有想清楚。
代码:
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
typedef long long inta;
inta dp[1<<18][101];
int b[20];
int p[20];
int m;
int main()
{
string str;
cin>>str>>m;
int n=str.length();
for(int i=0;i<n;i++)
b[i]=str[i]-48;
memset(dp, 0, sizeof(dp));
dp[0][0]=1;
for(int i=0;i<=(1<<n)-1;i++)
{
memset(p,-1 ,sizeof(p));
for(int j=0;j<n;j++)
if(!(i&(1<<j))) p[b[j]]=j;
for(int j=0;j<m;j++)
{
if(i)
{
for(int k=0;k<10;k++)
if(p[k]>=0) dp[i|(1<<p[k])][(10*j+k)%m]+=dp[i][j];
}
else
{
for(int k=1;k<10;k++)
if(p[k]>=0) dp[i|(1<<p[k])][(10*j+k)%m]+=dp[i][j];
}
}
}
cout<<dp[(1<<n)-1][0]<<endl;
}