码迷,mamicode.com
首页 > 其他好文 > 详细

BZOJ 1072 [SCOI2007]排列perm

时间:2015-12-20 22:28:28      阅读:158      评论:0      收藏:0      [点我收藏+]

标签:

考虑到s的长度特别小,只有10,可以考虑状压dp。

设F[S][d]表示当选了集合S(用二进制压位表示)中的所有位置,对D取模的结果为d的方案总数;不难想到转移和初始化。

初始化:F[0][0]=1  0在这里表示空集合

转移:F[S][(d * 10 + s[i]-‘0‘) % D]=sum{F[S0][d]}  S0是S的一个子集,并且刚好只比S少一个元素i

注意,重复的数字被算了多遍。样例当中就有。因此最后的答案要除以所有重复的数字个数的阶乘。

看代码就明白啦~

(再补充一个要用到状压技巧的题目特别是状压dp题目中常用的结论:当S‘是S的一个真子集时,必定有S‘<S。这样就可以在dp的时候确定枚举的顺序了。)

 

技术分享
#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const size_t Max_Len(20);
const size_t Max_D(1050);
const size_t Max_Bit(1500);

unsigned int T;

unsigned int D;
unsigned int Len;
char Str[Max_Len];
unsigned int F[Max_Bit][Max_D];

unsigned int Cnt[10];

int main()
{
    cin >> T;
    while (T--)
    {
        memset(F, 0, sizeof(F));
        memset(Cnt, 0, sizeof(Cnt));
        
        cin >> Str >> D;
        Len = strlen(Str);
        for (unsigned int i = 0;i != Len;++i)
            ++Cnt[Str[i] - 0];
        
        F[0][0] = 1;
        for (unsigned int S = 1;S < (1 << Len);++S)
            for (unsigned int i = 0;i < Len;++i)
                if (S & (1 << i))
                    for (unsigned int d = 0;d < D;++d)
                        F[S][(d * 10 + Str[i] - 0) % D] += F[S ^ (1 << i)][d];
        unsigned int &Ans = F[(1 << Len) - 1][0];
        for (unsigned int i = 0;i != 10;++i)
            for (unsigned int g = 2;g <= Cnt[i];++g)
                Ans /= g;
        cout << Ans << endl;
    }
    
    return 0;
}
BZOJ 1072

 

BZOJ 1072 [SCOI2007]排列perm

标签:

原文地址:http://www.cnblogs.com/Created-equal/p/5060493.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!