bzoj1072排列
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1072
好像是这方面的裸题。
整除k 要想转移需要记录下 达到模k所有余数 的方案数。
为了生成排列,状压记录当前已用了原数组中的哪些位置;
因为是无顺序地取用的,所以可以有顺序地放在目标数组中,即续在上一个数后面;所以 导致的余数 就是 之前余数*10+这个数。
另一种想法是有顺序取用、无顺序放置;即用了前 i 个数,状压记录放在了哪些位置上;新加入一个数的贡献是 之前余数+这个数*1ek。
感觉第一种比较方便?
循环的顺序需要注意!要把状压的状态 j 放在最外面,而不是当前位置 i 。
dp的初值需要想想。
对于值相等的一些数字,在排列中无区别,dp的时候却有区别地对待了。
只需要对于每一组,答案除去它们的排列数(即阶乘)即可。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int t,a[15],cnt,k,d[1030][1005],lm,ans,num[15]; char ch; int kj[15]={1,1,2,6,24,120,720,5040,40320,362880,3628800}; int main() { scanf("%d",&t); while(t--) { cnt=0;ans=0; memset(d,0,sizeof d); memset(num,0,sizeof num); d[0][0]=1;///// scanf(" %c",&ch); while(ch>='0'&&ch<='9') { a[++cnt]=ch-'0'; num[a[cnt]]++; ch=getchar(); } lm=(1<<cnt); scanf("%d",&k); for(int j=0;j<lm;j++)//当前取用了原串中哪些位置(按顺序后续着放下) for(int i=1;i<=cnt;i++) if((j&(1<<(i-1)))==0) for(int l=0;l<k;l++) d[j|(1<<(i-1))][(l*10+a[i])%k]+=d[j][l]; ans=d[lm-1][0]; for(int i=0;i<=9;i++)ans/=kj[num[i]]; printf("%d\n",ans); } return 0; }