• 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
    ");
    			else printf("0
    ");
    			continue;
    		}
    		if(n == 1) {
    			printf("%d
    ", 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
    ", 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
    ", ans);
    		}
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    POJ 1315 Don't Get Rooked
    POJ 2051 Argus
    POJ 1942 Paths on a Grid
    POJ 2151 Check the difficulty of problems
    POJ 3349 Snowflake Snow Snowflakes
    POJ 1753 Flip Game
    POJ 2392 Space Elevator
    POJ 2385 Apple Catching
    POJ 2356 Find a multiple
    POJ 2355 Railway tickets
  • 原文地址:https://www.cnblogs.com/AOQNRMGYXLMV/p/5357413.html
Copyright © 2020-2023  润新知