【51nod1597有限背包计数问题】

之前学习多维背包的目的就是这道题,,结果一个细节没处理好,瞬间爆炸 51nod 1597 (据说这道题可以FFT卷积nlogn?)

题目

你有一个大小为n的背包,你有n种物品,第i种物品的大小为i,且有i个,求装满这个背包的方案数有多少 两种方案不同当且仅当存在至少一个数i满足第i种物品使用的数量不同 范围 <=1e5

解法

感觉就是一个多维背包?仔细一算n^2呃呃呃,爆炸了 实际上这个题n容积n物品,第i物品的大小为i,个数为i,这就有一些很好的性质。 如果我们将其以sqrt(n)分块,将这道题分开成两道题,就变成了求前sqrt(n)种物品的方案,这个就利用单调队列搞多维背包就可以了。 而n-sqrt(n)种物品的部分,由于我们知道其大小>sqrt(n),个数>sqrt(n)我们完全可以将其当作物品无限的背包来处理。于是我们有一个dp状态f[i][j]表示选了前i个物品,这i个物品和为j,根据这个我们可以往后推两种状态 dp[i][j]--->dp[i-1][j+(sqrt(n)+1)]我们增加一个大小为sqrt(n)+1的物品 --->dp[i][j-i]我们将这i个物品的大小都增加i 为什么这样成立,感性理解一下(雾),确实这样就可以表示出所有的状态情况(虽然这个状态和方程感觉确实很迷啊,,我就算想到了状态也不知道怎么推啊),之后一个滚动数组dp就跑出来了。 果然太弱了,还需更强啊sto sto orz orz code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#define mmp make_pair 
using namespace std;
const int maxn = 100005;
const int mod = 23333333;
int n;
int dp[maxn];
int f[2][maxn];
deque<pair<int,int> > q;
int main()
{
	scanf("%d",&n);
	int sq = sqrt(n);
	f[0][0]=1; dp[0]=1;
	for(int i=1;i<=sq;i++)
	{
		int t = i&1;
		for(int j=0;j<=n;j++)
		{
			f[t][j]=0;
			if(j>=(sq+1)) f[t][j]+=f[t^1][j-(sq+1)];
			f[t][j]%=mod;
			if(j>=i) f[t][j]+=f[t][j-i];
			f[t][j]%=mod;
			dp[j]+=f[t][j];
			dp[j]%=mod;
		}
	}

	for(int i=1;i<=sq;i++)
	{
		for(int o=0;o<i;o++)
		{
			while(q.size()) q.pop_back();
			int sum = 0;
			for(int j=o;j<=n;j+=i)
			{
				sum+=dp[j]; q.push_back(mmp(dp[j],j));
				while(q.size()&&q.front().second<j-i*i)
				{ sum-=q.front().first; q.pop_front(); }
				sum=(sum%mod+mod)%mod;
				dp[j]=sum;
				dp[j]%=mod;
			}
		//	cout<<dp[n]<<endl;
		}
	}
	printf("%d",dp[n]);
}
 
posted @ 2018-08-02 00:02  Newuser233  阅读(13)  评论(0)    收藏  举报