【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]); }