zoukankan      html  css  js  c++  java
  • 51Nod-1597 有限背包计数问题 根号分治

    51Nod-1597 有限背包计数问题 根号分治

    题意

    有一个大小为(n)的背包,第(i)种物品的大小是(i),且有(i)

    求装满背包的方案数是多少个

    [1 leq n leq 1e5 ]

    分析

    容易发现当(i > sqrt{n})的时候个数大小相当于没有限制,因此这道题可以往根号分治的角度去思考

    • (i leq sqrt{n})

    考虑直接(DP)

    [dp[i][j] = sum dp[i-1][j-k*i] ]

    这个东西可以通过维护$ % i$ 的前缀和(O(1))转移,复杂度(O(nsqrt{n}))

    • (i > sqrt{n})

    个数没有限制。

    这里比较巧妙:把原问题转化为每次进行如下操作

    1.加入一个最小的物品(大小为(sqrt{n} + 1))

    2.所以物品大小+1

    容易证明这样的操作和原方案数是一一对应的

    这样也可以简单的(DP)来实现

    最后只需要分别统计一下即可

    代码

    const int maxn = 1e5 + 5;
    
    int f1[2][maxn],f2[2][maxn];
    
    
    int main(){
    	int n = rd();
    	int m = sqrt(n);
    	f1[0][0] = 1;
    	for(int i = 1;i <= m;i++){
    		VI sum(i);
    		for(int j = 0;j <= n;j++){
    			add(sum[j % i],f1[i & 1 ^ 1][j]);
    			if(j - i * (i + 1) >= j % i) add(sum[j % i],MOD - f1[i & 1 ^ 1][j - i  * (i + 1)]);
    			f1[i & 1][j] = sum[j % i];
    		}
    	}
    	VI sum(n + 1);
    	sum[0] = 1;
    	f2[0][0] = 1;
    	for(int i = 1;i <= m;i++){
    		memset(f2[i & 1] + (i - 1) * (m + 1),0,(m + 1) << 2);
    		for(int j = i * (m + 1);j <= n;j++){
    			f2[i & 1][j] = (f2[i & 1][j - i] + f2[i & 1 ^ 1][j - (m + 1)]) % MOD;
    			add(sum[j],f2[i & 1][j]);
    		}
    	}
    	int ans = 0;
    	for(int i = 0;i <= n;i++)
    		add(ans,mul(f1[m & 1][i],sum[n - i]));
    	printf("%d",ans);
    }
    
  • 相关阅读:
    非模式窗体和模式窗体(转
    一次性帮你解决毕业论文的所有排版问题
    c# Wndproc的使用方法
    ref和out 转
    c# 基本语法(转)
    U盘装/虚拟光驱 装双系统
    U盘启动盘 装系统
    Hibernate查询条件封装对象Expression介绍 Hi
    asp.net实现 gridview 鼠标单击任意字段选中一行 ,并获取数据 Hi
    C#如何取出非公共成员 Hi
  • 原文地址:https://www.cnblogs.com/hznumqf/p/15260141.html
Copyright © 2011-2022 走看看