zoukankan      html  css  js  c++  java
  • HDU 6042

    /*
    HDU 6042 - Journey with Knapsack [ 生成函数,五边形定理 ]  |  2017 Multi-University Training Contest 1
    题意:
    	n种物品,每种 a[i] 个,体积为 i,m 个武器,每个体积为 b[i]
    	求2*n大小的背包里只能装一个武器,任意食物的方案数
    	限制条件:
    		a[i]各不相同且 0 <= a[1] <= a[2] <= ... a[n] <= 2*n
    		n <= 5e4
    分析:
    	先不考虑武器,求出任意i <= 2*n 的放食物的方案数ans[2*n],答案即为 ∑ ans[2*n - b[i]]
    	其实本质和整数划分相同,限制了每个数字选择的次数
    	考虑选择食物的生成函数:
    		第i种食物的贡献因子 f(x) = 1 + x^i + x^2i + ... + x^(a[i]*i) = ( 1 - x^((ai+1)*i) ) / ( 1 - x^i )
    	故	F(x) =  ∏ f(i) [1<=i<=n] 
    			 = ∏ (1 - x^((ai+1)*i)) / (1-x^i) [1<=i<=n]
    			 = ∏ (1 - x^((ai+1)*i)) [1<=i<=n]  *  ∏ 1/(1-x^i) [1<=i<=n]
    
    	研究一下乘号左边这一项累乘:
    		假设已求得乘号右边这一项 F'(x) = ∏ 1/(1-x^i) [1<=i<=2*n] = ∑ dp[i]*x^i [1<=i<=2*n] 
    		则两边多项式合并时,相当于一个n项的多项式(右边) 和 n个两项的多项式(左边)相乘
    		假设左边第i项 (1 - x^((ai+1)*i)) 与 F'(x) 合并,则:
    			(1 - x^((ai+1)*i)) * ( ∑ dp[j]*x^j [1<=j<=2*n] ) =  ∑ dp[j]*x^j [1<=j<=n] - ∑ dp[j-(ai+1)*i] * x^j [(ai+1)*i) <= j <= 2*n+(ai+1)*i)] (多项式右移(ai+1)*i)次)
    		由于体积只有2*n大小,故x^(2*n+1)项及以后无意义
    			故上式 = ∑ dp[j]*x^j [1<=j<=n]  -  ∑ dp[j-(ai+1)*i] * x^j [(ai+1)*i) <= j <= 2*n] 
    				   = ∑ dp[j]*x^j [1<=j< (ai+1)*i)]  +  ∑ (dp[j]- dp[j-(ai+1)*i])*x^j [(ai+1)*i) <= j <= 2*n] 
    		即对于所有 (ai+1)*i <= j <= 2*n 的项,执行操作:
    			dp[j] = dp[j] - dp[j-a(i+1)*i] (按01背包形式,至高往低)
    		
    		由于合并后 F'(x) 形式不变,故可循环合并 n 次
    		单次合并复杂度 O(2*n - a(i+1)*i) = O(n)
    		根据限制条件 0 <= a[1] <= a[2] <= ... a[n] <= 2*n ,可推得 ai >= i-1, (ai+1)*i >= i^2 (鸽巢原理)
    		故 (ai+1)*i <= 2*n 的项只有 O(n^0.5) 项
    		故合并总时间复杂度 O(n^1.5)
    
    	研究一下乘号右边这一项累乘:
    		其形式与无限制的整数划分的方案数的生成函数相同,由于目前只有n项累乘,按题意先补齐至2n项
    		F'(x) = ∏ 1/(1-x^i) [1<=i<=n] 
    			  = ∏ 1/(1-x^i) [1<=i<=2n]  *  ∏ (1-x^i) [n+1<=i<=2n]
    		
    		乘号左边这一项 P(x) 即2*n以内无限制的整数划分的方案数,根据五边形定理可以在 O(n^1.5) 预处理出
    		
    		观察乘号右边那一项,虽然形式与上面讨论的累乘形式相近,但可以分析暴力合并复杂度 O(n^2)
    		打开这个累乘,由于 n+1<=i<=2n ,任意两项 x^i * x^j = x^(i+j) ,此时i+j > 2*n
    		故去掉指数为 2n 以上的无效项后:
    		∏ (1-x^i) [n+1<=i<=2n]  =  1 - ∑x^i [n+1<=i<=2n] 
    		F'(x) = P(x)  *  ( 1 - ∑x^i ) [n+1<=i<=2n] 
    		令 	P(x) = ∑ dp[i]*x^i [1<=i<=2n],考虑合并:
    		F'(x) =   ∑ dp[i] * x^i [1<=i<=2n] 
    				- ∑ dp[i-n-1] * x^i [n+1<=i<=2n]
    				- ∑ dp[i-n-2] * x^i [n+2<=i<=2n]
    				- ∑ dp[i-n-3] * x^i [n+3<=i<=2n]
    				- ...
    			  =   ∑ dp[i] * x^i [1<=i<=n]
    				+ ∑ x^(n+1)  *  (dp[n+1] - dp[0])
    				+ ∑ x^(n+2)  *  (dp[n+1] - dp[0] - dp[1])
    				+ ∑ x^(n+2)  *  (dp[n+1] - dp[0] - dp[1] - dp[2])
    				... 
    		即对于所有 n+1 <= j <= 2*n 的项,执行操作:
    			dp[j] = dp[j] - sum[j-n-1],  sum[]为dp[]的前缀和
    		合并复杂度O(n)
    	
    	总时间复杂度O(n^1.5)
    */
    #include <bits/stdc++.h>
    using namespace std;
    #define LL long long
    const LL MOD = 1e9+7;
    const int N = 100005;
    LL Qn[3000], f[N];//五边形数,整数划分生成函数
    void Init() {
        for (int i = 1; i <= 2000; i += 2) {
            LL n = (i+1)/2;
            Qn[i] = (3*n*n - n) / 2;
            Qn[i+1] = (3*n*n + n) / 2;
        }
        f[0] = 1;
        for (int i = 1; i < N; i++)
            for (int j = 1, r = 1; j <= 2000; j += 2, r *= -1) {
                if (i - Qn[j] < 0) break;
                f[i] = (f[i] + f[i-Qn[j]] * r + MOD) % MOD;
                if (i - Qn[j+1] < 0) break;
                f[i] = (f[i] + f[i-Qn[j+1]] * r + MOD) % MOD;
            }
    }
    LL dp[N];
    int n, m, tt;
    int main()
    {
        Init(); tt = 0;
        while (~scanf("%d%d", &n, &m))
        {
            for (int i = 0; i <= n<<1; i++) dp[i] = f[i];
            for (int i = 1; i <= n; i++)
            {
                LL a, b; scanf("%lld", &a); b = i*(a+1);
                for (int j = n<<1; j >= b; j--)
                    dp[j] = (dp[j] + MOD - dp[j-b]) % MOD;
            }
            for (int j = n+1, sum = 0; j <= n<<1; j++)
            {
                sum = (sum + dp[j-n-1]) % MOD;
                dp[j] = (dp[j] + MOD - sum) % MOD;
            }
            LL ans = 0;
            while (m--)
            {
                int x; scanf("%d", &x);
                ans = (ans + dp[2*n-x]) % MOD;
            }
            printf("Case #%d: %lld
    ", ++tt, ans % MOD);
        }
    }
    

     * 修正了写错的公式

    我自倾杯,君且随意
  • 相关阅读:
    Drupal 学习笔记 (4)
    property和field有什么区别
    Razor4Orchard v1.2
    Mono喜迁新家http://www.xamarin.com/
    jq中获取属性名的方法
    jquery中的文档操作之五
    PHP基础知识之三
    php基础知识之一
    注意点
    jquery中ajax的学习之$.ajax()
  • 原文地址:https://www.cnblogs.com/nicetomeetu/p/7258131.html
Copyright © 2011-2022 走看看