zoukankan      html  css  js  c++  java
  • Codechef APRIL14 ANUCBC Cards, bags and coins 背包DP变形

      题目大意

        有n个数字,选出一个子集,有q个询问,求子集和模m等于0的方案数%1000000009。(n <= 100000,m <= 100,q <= 30)

      假设数据很小,我们完全可以做一个背包。

      我们沿着背包的思路,看能不能给物品分一下类,由于m比较小,完全按N个数字模M后的值进行分类,这样就变成了一个多重背包的问题。(转移时要乘上一个组合数)

      这时候的时间复杂度是n*m,还是不能过。

      对于DP时所枚举到的模m后余数j,它所进行的状态转移是一定的,如果把这些转移先预处理出来,时间复杂度就能得到有效减小。

      分析一下,转移方程是 F[i][j] = sigma(F[i][(j-i*k+m)%m]*C(count[i], k))%MOD,可以发现,(j-i*k+m)%m的值最多也只有m个,根本不需要枚举count[i]个这么多。

      设t = (i*k)%m,G[i][t] = sigma(C(count[i], k)) (k = 0..count[i] 且 i*k%m == t),G数组可以在O(n)的时间内预处理出来。

      新的转移方程就可以整理为 F[i][j] = sigma(F[i][(j-t+m)%m]*G[i][t])%MOD。 总的时间复杂度为O(n+m^3)。

      问题还没有结束,G数组的计算也需要一定的技巧。如果计算每个G[i][t]的值都算一次逆元和组合数,时间复杂度起码要加上一个log,会TLE。

      仔细分析可以发现,所计算的组合数C(count[i], k) (k = 0..count[i]),k是严格递增的,设temp = C(count[i], k-1),则C(count[i], k) = temp*(count[i]-(k-1))*inv[k]。

      inv数组可以利用逆元打表O(n)的方法来实现,具体可参考博文:http://blog.csdn.net/guhaiteng/article/details/52123385

      程序对拍过,但运行速度较慢。

      

    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    typedef long long LL;
    const int maxn = 100005;
    const int MOD = 1000000009;
    int n, m, Q;
    int ccount[105];
    LL g[105][105], inv[maxn], f[105][105];
    int a[maxn], ans;
    
    void add(LL &x, LL y)
    {
        x += y;
        if (x >= MOD)
            x -= MOD;
    }
    
    void prepare()
    {
        for (int i = 0; i < m; ++i)
            ccount[i] = 0;
        for (int i = 1; i <= n; ++i)
        {
            int temp = (a[i]%m+m)%m;
            ccount[temp] ++;
        }
        for (int i = 0; i < m; ++i)
            for (int j = 0; j < m; ++j)
                g[i][j] = 0;
        for (int i = 0; i < m; ++i)
        {
            LL temp = ccount[i];
            add(g[i][0], 1);
            add(g[i][i%m], ccount[i]);
            for (int j = 2; j <= ccount[i]; ++j)
            {
                (temp *= (ccount[i]-(j-1))) %= MOD;
                (temp *= inv[j]) %= MOD;
                add(g[i][(i*j)%m], temp);
            }
        }
    }
    
    void dp()
    {
        memset(f, 0, sizeof(f));
        f[0][0] = g[0][0];
        for (int i = 1; i < m; ++i)
            for (int j = 0; j < m; ++j)
                for (int k = 0; k < m; ++k)
                {
                    LL temp = f[i-1][(j-k+m)%m]*g[i][k]%MOD;
                    add(f[i][j], temp);
                }
        ans = f[m-1][0];
    }
    
    int main()
    {
        inv[1] = 1;
        for (int i = 2; i <= 100000; ++i)
            inv[i] = LL(MOD-MOD/i)*inv[MOD%i]%MOD;
        int Task;
        scanf("%d", &Task);
        while (Task --)
        {
            scanf("%d %d", &n, &Q);
            for (int i = 1; i <= n; ++i)
                scanf("%d", &a[i]);
            while (Q --)
            {
                scanf("%d", &m);
                prepare();
                dp();
                printf("%d
    ", ans);
            }
        }
        return 0;
    }
    Nothing is impossible!
  • 相关阅读:
    Java中List和ArrayList的区别(转)
    Java的JDBC事务详解(转)
    Java JDBC批处理插入数据操作(转)
    celery入门
    Python装饰器与面向切面编程
    python 中类方法@classmethod
    Virtualenv: 一个Python环境管理工具(windown版本)
    python操作excel之 模块 xlrd
    django多条件筛选搜索(项目实例)
    jQuery选择器总结
  • 原文地址:https://www.cnblogs.com/-ZZB-/p/6388042.html
Copyright © 2011-2022 走看看