zoukankan      html  css  js  c++  java
  • HDU 4623 Crime (状压DP + 数学优化)

    题目:传送门

    题意:问存在多少 1 ~ n 的排列满足任意相邻的两个数互质,输出答案取余 mod。

          1 <= n <= 28, 1 <= mod <= 30000

    思路:很容易想到状压DP, dp[ i ][ j ]其中 i 是最后一个数要填的数,j 是当前使用过的数的状态,每一个二进制位对应一个数。也就是还没填 i 时使用过的数的状态为 j 的满足条件的方案数。

          然后发现 2^28 开不了数组,没法做,得想办法优化一下。

       我们对那些拥有相同质因子的数分为同一类,例如:2和4和8是同一类,3和9是同一类,但是,3 和 6 不是同一类。然后再将 1 17 19 23 分为同一类,因为它们和谁都互质。分完之后,总共最多有 15 组,那就可以开数组了,对每个数,用不同的进制表示,详见代码。

    #include <bits/stdc++.h>
    #define LL long long
    #define mem(i, j) memset(i, j, sizeof(i))
    #define rep(i, j, k) for(int i = j; i <= k; i++)
    #define dep(i, j, k) for(int i = k; i >= j; i--)
    #define pb push_back
    #define make make_pair
    #define INF INT_MAX
    #define inf LLONG_MAX
    #define PI acos(-1)
    using namespace std;
    
    const int N = 2e6 + 5;
    
    short dp[19][N];
    int c[30], vis[N], statu[30], bs[30], G[30][30];
    int n, mod, up;
    int cnt;
    int tmp[10] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
    
    int get(int i, int x) {
        int res = (x % bs[i + 1]) / (bs[i]);
        return res;
    }
    
    void dfs(int s, int x) {
        if(s == 0) {
            dp[x][0] = 1; return ;
        }
        if(dp[x][s] != -1) return ;
        dp[x][s] = 0;
        rep(i, 1, cnt) {
            if(G[x][i] && get(i, s) >= 1) {
                dfs(s - bs[i], i);
                dp[x][s] = ((int)dp[x][s] + dp[i][s - bs[i]]) % mod;
            }
        }
    }
    
    void solve() {
    
        scanf("%d %d", &n, &mod);
        mem(dp, -1); mem(vis, 0);  mem(statu, 0); mem(c, 0);
        mem(G, 0);
        int ans = 0; cnt = 0;
        statu[++cnt] = 0;
        c[cnt] = 1;
    
        rep(i, 2, n) { /// 分类
            int st = 0;
            if(i == 17 || i == 19 || i == 23) {
                c[1]++; continue;
            }
            rep(j, 0, 5) {
                if(i % tmp[j] == 0) {
                    st |= (1 << j);
                }
            }
            if(!vis[st]) {
                statu[++cnt] = st;
                c[cnt] = 1;
                vis[st] = cnt;
            }
            else c[vis[st]]++;
        }
    
        rep(i, 1, cnt) rep(j, 1, cnt) { /// 判断是否互质
            if((statu[i] & statu[j]) == 0) G[i][j] = 1;
        }
    
        up = 0; bs[1] = 1;
        rep(i, 1, cnt) { /// 每一位都有各自的进制
            bs[i + 1] = bs[i] * (c[i] + 1);
            up += bs[i] * c[i];
        }
    
        rep(i, 1, cnt) {
            dfs(up - bs[i], i);
            ans = ((int)ans + dp[i][up - bs[i]]) % mod;
        }
    
        rep(i, 1, cnt) {
            while(c[i] > 1) { /// 同一类的还有先取后取之分,要乘 c[i]的阶乘
                ans = ((int)ans * c[i]) % mod;
                c[i]--;
            }
        }
    
        printf("%d
    ", ans);
    
    }
    
    int main() {
    
        int _; scanf("%d", &_);
        while(_--) solve();
    
    
        return 0;
    
    }
    一步一步,永不停息
  • 相关阅读:
    【第40套模拟题】【noip2011_mayan】解题报告【map】【数论】【dfs】
    【模拟题(63550802...)】解题报告【贪心】【拓扑排序】【找规律】【树相关】
    【模拟题(电子科大MaxKU)】解题报告【树形问题】【矩阵乘法】【快速幂】【数论】
    IMemoryBufferReference and IMemoryBufferByteAccess
    SoftwareBitmap and BitmapEncoder in Windows.Graphics.Imaging Namespace
    Windows UPnP APIs
    编译Android技术总结
    Windows函数转发器
    Two Ways in Delphi to Get IP Address on Android
    Delphi Call getifaddrs and freeifaddrs on Android
  • 原文地址:https://www.cnblogs.com/Willems/p/12400595.html
Copyright © 2011-2022 走看看