HDU 5657 CA Loves Math 状压DP + 枚举

题意:

给出\(A(2 \leq A \leq 11), n(0 \leq n \leq 10^9), k(1 \leq k \leq 10^9)\)
求区间\([1, A^n]\)中各个数字互不相同的\(A\)进制数而且是\(k\)的倍数的个数。

分析:

如果\(n > A\),根据抽屉原理,\(n\)\(A\)进制数一定会有重复的数字。
所以下面只讨论\(n \leq a\)的情况。

对于\(k\)的大小,分别使用不同的算法:

  • \(k\)比较小的时候,用状压DP:\(d(S, x)\)表示当前数字集合为\(S\)且模\(k\)\(x\)的数字的个数。
    在数字的末位添加一个未在集合\(S\)出现的数字\(i\),则转移到状态\(d(S',(x \cdot A + i) % k)\)

  • \(k\)比较大的时候,直接暴力统计。枚举区间内所有\(k\)的倍数,判断一下如果没有重复数字答案+1。

第一种算法的复杂度为\(O(2^A \cdot k \cdot A)\),状态数乘转移数。
第二种算法复杂度为\(O(A^n/k \cdot A)\),枚举次数乘判断时间。

找到一个合适的边界使得时限都能承受两种算法,测试了一下\(25000\)\(30000\),运行时间都是\(3s\)多,可以AC。

PS:\(n=0\)\(n=1\)这些边界情况最好特判一下。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;

int A, n, k;
const int maxk = 30000;
const int maxa = 11;

LL d[1 << maxa][maxk];

int bitcount(int x) {
    int ans = 0;
    while(x) { if(x & 1) ans++; x >>= 1; }
    return ans;
}

bool check(LL x) {
    int S = 0;
    while(x) {
        int t = x % A;
        x /= A;
        if(S & (1 << t)) return false;
        S |= (1 << t);
    }
    return true;
}

int main()
{
    int T; scanf("%d", &T);
    while(T--) {
        scanf("%d%d%d", &A, &n, &k);

        if(n == 0) {
            if(k == 1) printf("1\n");
            else printf("0\n");
            continue;
        }
        if(n == 1) {
            printf("%d\n", A / k);
            continue;
        }

        //DP
        if(k < maxk) {
            memset(d, 0, sizeof(d));
            for(int i = 1; i < A; i++) d[1 << i][i % k] = 1;
            for(int S = 0; S < (1 << A); S++) if(bitcount(S) < n) {
                for(int x = 0; x < k; x++) if(d[S][x]) {
                    for(int i = 0; i < A; i++) if(!(S&(1<<i))) {
                        d[S|(1<<i)][(x*A+i)%k] += d[S][x];
                    }
                }
            }
            LL ans = 0;
            for(int S = 0; S < (1 << A); S++)
                if(d[S][0]) ans += d[S][0];
            printf("%lld\n", ans);
        } else {
            if(n > A) n = A;
            LL An = 1; //A^n
            for(int i = 1; i <= n; i++) An *= A;
            LL ans = 0;
            for(LL t = k; t <= An; t += k)
                if(check(t)) ans++;
            printf("%lld\n", ans);
        }
    }

    return 0;
}
posted @ 2016-04-05 23:24  AOQNRMGYXLMV  阅读(...)  评论(... 编辑 收藏