zoukankan      html  css  js  c++  java
  • 【51nod 1597】有限背包计数问题

    题目

    题目链接:http://www.51nod.com/Challenge/Problem.html#problemId=1597
    你有一个大小为 (n) 的背包,你有 (n) 种物品,第 (i) 种物品的大小为 (i),且有 (i) 个,求装满这个背包的方案数有多少。答案对 23333333 取模。
    两种方案不同当且仅当存在至少一个数 (i) 满足第 (i) 种物品使用的数量不同。
    (nleq 10^5)

    思路

    这题看上去就非常根号分治。考虑分别求出物品编号小于 (sqrt{n}) 和大于等于 (sqrt{n}) 时的背包,然后合并。
    首先来看小于 (sqrt{n})。此时每个物品是有不能取超过 (i) 个的限制的。由于物品数只有 (O(sqrt{n})),所以可以直接设 (f[i][j]) 表示前 (i) 个物品,使用了 (j) 的容量的方案数。
    然后有转移

    [f[i][j]=sum^{i}_{k=0}f[i-1][j-ki] ]

    就是枚举用多少个物品 (i)。这个玩意前缀和优化一下就好了。
    然后再看编号大于等于 (sqrt{n}) 的物品,我么发现这些物品一定是取不到上限的,所以其实等价于一个完全背包。
    有一个很经典的 trick:假设我们有一个序列 (a),我们可以通过如下操作构成这个序列:添加一个大小为 (1) 的物品,或者将所有物品大小加一。不难发现这个操作顺序与序列 (a) 是一一对应的。
    同理,设 (g[i][j]) 表示选择了 (i) 个编号超过 (sqrt{n}) 的物品,容量之和为 (j) 的方案数。那么考虑添加一个大小为 (sqrt{n}) 的物品,或者将所有物品大小加一,有

    [g[i][j]=g[i][j-i]+g[i-1][j-sqrt{n}] ]

    最后合并背包即可。注意需要滚动数组。
    时间复杂度 (O(nsqrt{n}))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=100010,M=320,MOD=23333333;
    int n,ans,f[2][N],g[2][N],h[N];
    
    int main()
    {
    	scanf("%d",&n);
    	f[0][0]=g[0][0]=1;
    	for (int i=1;i<M;i++)
    	{
    		int id=i&1;
    		memset(f[id],0,sizeof(f[id]));
    		for (int j=0;j<=n;j++)
    		{
    			h[j]=(f[id^1][j]+((j>=i) ? h[j-i] : 0))%MOD;
    			f[id][j]=(h[j]-((j>=i*(i+1)) ? h[j-i*(i+1)] : 0))%MOD;
    		}
    	}		
    	memset(h,0,sizeof(h));
    	for (int i=1;i<=M;i++)
    	{
    		int id=i&1;
    		memset(g[id],0,sizeof(g[id]));
    		for (int j=0;j<=n;j++)
    		{
    			if (j>=i) g[id][j]=(g[id][j]+g[id][j-i])%MOD;
    			if (j>=M) g[id][j]=(g[id][j]+g[id^1][j-M])%MOD;
    			h[j]=(h[j]+g[id][j])%MOD;
    		}
    	}
    	h[0]=1;
    	for (int i=0;i<=n;i++)
    		ans=(ans+1LL*f[1][i]*h[n-i])%MOD;
    	printf("%d",(ans%MOD+MOD)%MOD);
    	return 0;
    }
    
  • 相关阅读:
    redis命令
    linux命令行任务管理
    tomcat修改内存
    Python调用shell
    取消myeclipse自动进入workspace
    解决Myeclipse编译不生成.class文件问题
    Manacher回文串算法学习记录
    青少年如何使用 Python 开始游戏开发
    对 Linux 专家非常有用的 20 个命令
    对中级 Linux 用户非常有用的 20 个命令
  • 原文地址:https://www.cnblogs.com/stoorz/p/14700336.html
Copyright © 2011-2022 走看看