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;
}

 

posted on 2018-03-30 01:16  Narh  阅读(90)  评论(0编辑  收藏  举报

导航