[51nod1597]有限背包计数问题

试题描述

你有一个大小为n的背包,你有n种物品,第i种物品的大小为i,且有i个,求装满这个背包的方案数有多少

两种方案不同当且仅当存在至少一个数i满足第i种物品使用的数量不同

输入

第一行一个正整数n
1<=n<=10^5

输出

一个非负整数表示答案,你需要将答案对23333333取模

输入示例

3

输出示例

2

数据规模及约定

见“输入

题解

分块的形式真是多种多样,这题就是一个分块 dp。

这里分块的意思是对 1~i 这 n 种物品分类讨论。即对体积小于等于 sqrt(n) 的部分使用一种 dp 方法解决,对于体积大于 sqrt(n) 的部分使用另一种 dp 方法解决,最后由于“小于等于 sqrt(n) 和大于 sqrt(n) 的部分”没有交集,相互独立,可以使用乘法原理进行合并。

首先考虑只选用体积小于等于 sqrt(n) 的物品放入背包,这是一个多重背包问题,设 f(i, j) 表示考虑前 i 物品组成体积 j 的方案数,由于这题特殊性(体积为 i 的物品有 i 个),我们可以对 j mod i 将 f(i, j) 分类(共有 i 类),然后对于 j mod i = k 的类别计算一下 f(i, j) 前缀和 sum[j](共有 [n / i] 个前缀和),更新 f(i+1, j) 的时候就用 sum[j] - sum[j-i*i] 就好了(记得判断 j - i * i 会不会越界)

然后考虑体积大于 sqrt(n) 的物品,这个时候可以不考虑个数限制,因为每个物品不会选择超过 sqrt(n) 个。考虑另一种 dp,g(i, j) 表示选择了 i 个(注意是“个”不是“种”,显然 i ≤ sqrt(n))物品,组成体积 j 的方案数。这个 dp 中我们只关心体积最小的物品,有两种转移:一,放入一个新的体积最小的物品(体积为 sqrt(n) + 1);二,所有物品体积 +1。

注意:两种 dp 都需要开滚动数组。

最后用乘法原理乘起来,累加,这题就解决了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <cmath>
using namespace std;

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(x == ‘-‘) f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
	return x * f;
}

#define maxn 100010
#define MOD 23333333
#define LL long long

int n, f[maxn], g[2][maxn], gsum[maxn];

int main() {
	n = read();
	int m = (int)sqrt(n + .5);

	f[0] = 1;
	for(int i = 1; i <= m; i++)
		for(int mod = 0; mod < i; mod++) {
			int sum = 0, j, cnt = 0;
			for(j = mod; j <= n; j += i) {
				sum += f[j];
				if(sum >= MOD) sum -= MOD;
				if(++cnt > i) {
					sum -= f[j-i*i];
					if(sum < 0) sum += MOD;
					cnt--;
				}
			}
			for(j -= i; j >= mod; j -= i) {
				sum -= f[j];
				if(sum < 0) sum += MOD;
				if(j >= i * i) {
					sum += f[j-i*i];
					if(sum >= MOD) sum -= MOD;
				}
				f[j] += sum;
				if(f[j] >= MOD) f[j] -= MOD;
			}
		}
//	for(int i = 0; i <= n; i++) printf("%d%c", f[i], i < n ? ‘ ‘ : ‘\n‘);

	int curg = 0;
	g[0][0] = 1;
	for(int i = 0; i <= m; i++, curg ^= 1) {
		memset(g[curg^1], 0, sizeof(g[curg^1]));
		for(int j = 0; j <= n; j++) if(g[curg][j]) {
//			printf("g %d %d: %d\n", i, j, g[curg][j]);
			gsum[j] += g[curg][j];
			if(gsum[j] >= MOD) gsum[j] -= MOD;
			if(i < m && j + m + 1 <= n) {
				g[curg^1][j+m+1] += g[curg][j];
				if(g[curg^1][j+m+1] >= MOD) g[curg^1][j+m+1] -= MOD;
			}
			if(i && j + i <= n) {
				g[curg][j+i] += g[curg][j];
				if(g[curg][j+i] >= MOD) g[curg][j+i] -= MOD;
			}
		}
	}
	curg ^= 1;

	int ans = 0;
	for(int i = 0; i <= n; i++) {
		ans += (LL)f[i] * gsum[n-i] % MOD;
		if(ans >= MOD) ans -= MOD;
	}
	printf("%d\n", ans);

	return 0;
}
时间: 2024-10-03 13:39:48

[51nod1597]有限背包计数问题的相关文章

51Nod 有限背包计数问题 题解报告

首先这道题理论上是可以做到O(nlogn)的,因为OEIS上有一个明显可以用多项式乘法加速的式子 但是由于模数不是很兹磁,所以导致nlogn很难写 在这里说一下O(n*sqrt(n))的做法 首先我们很容易发现当物品的大小>sqrt(n)的时候,物品数量的限制形同虚设 也就是说物品的大小>sqrt(n)的时候实际上是一个完全背包 而对于完全背包,有着另外一种做法(参照NOIP2001 数的划分) 由于我们知道假设我们只用>sqrt(n)的物品,我们最多使用sqrt(n)个物品 不妨设f[

2017/07/25 杂题(完全不可做题(划去))选讲

先膜一发主讲人@Antileaf 真是核平的一天那--大脑已经被蹂躏的死去活来了-- cogs2421 简单的Treap 链接:http://cogs.pro/cogs/problem/problem.php?pid=2421 题意:什么都给你了,建出Treap,输出先序遍历顺序. 实际上只是用了Treap的原则建树--先按照数值大小排序,然后按照建立笛卡尔树方法,维护单调栈,最大的全扔到右儿子去即可. 1 #include<iostream> 2 #include<cstdio>

51nod算法马拉松13

A 取余最长路 不难发现路径可以拆成三条线段,只要知道两个转折点的位置就能计算出答案. 设sum(i,l,r)表示第i行从l到r元素的和,则答案可以表示为sum(1,1,x)+sum(2,x,y)+sum(3,y,n)%p. 前缀和一下转化成(S3[n]-S3[y-1])+S2[y]+(S1[x]-S2[x-1])%p,从小到大枚举y,将所有(S1[x]-S2[x-1])扔到一个集合里,用个set就能轻松实现了. 时间复杂度为O(NlogN). #include<cstdio> #includ

HDU 2844 Coins (多重背包计数 空间换时间)

Coins Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 8999    Accepted Submission(s): 3623 Problem Description Whuacmers use coins.They have coins of value A1,A2,A3...An Silverland dollar. One

POJ 2229 sumset ( 完全背包 || 规律递推DP )

题意 : 给出一个数 n ,问如果使用不同 2 的幂的和来组成这个数 n 有多少种不同的方案? 分析 :  完全背包解法 将问题抽象==>有重量分别为 2^0.2^1.2^2-2^k 的物品且每种物品可无限取,问有多少种方案来填满容量为 n 的背包? 之前并不知道背包还能用来计数....... 有一道裸的背包计数问题可以作为练习 ==> HDU 1284 定义 dp[ i ][ j ] 为前 i 种物品组成总重量 j 的方案数为多少.初始化为 dp[ 0 ][ 0 ] = 1 其他为 0 则状

惨不忍睹考试合集

分别发的话实在太丑了以后就都堆这儿吧qwq 阿我到底什么时候才能强那么一点点呢qwq 太菜辣太菜辣qwq 4.1考试 愚人节快乐另外-- 怀念张国荣. 我们讲道理 为什么我在考场上全程脑子瓦特觉得sqrt(1e10)会比log(1e10)要小???? 黑人问号. 1.MR板子题 不想说什么 打了一半觉得log比sqrt大就去找规律了 结果成功跪烂 2.背包计数问题 全场最神 暴力n^3 带上一些没有什么特别用处的优化 n^2logn 还是基本没分 fhr dalao机智地蒙点打表很牛逼的中了(%

BZOJ 1739: [Usaco2005 mar]Space Elevator 太空电梯

题目 1739: [Usaco2005 mar]Space Elevator 太空电梯 Time Limit: 5 Sec  Memory Limit: 64 MB Description The cows are going to space! They plan to achieve orbit by building a sort of space elevator: a giant tower of blocks. They have K (1 <= K <= 400) differe

OI本月刷水记录

bzoj 1044 硬币购物 如果没有每个硬币的个数限制,这就是一个完全背包计数问题,现在我们注意到硬币个数很少,于是我们分别考虑一下每个硬币不符合条件的情况,容斥一下即可 bzoj 1225假如不考虑高精度的情况,那么转移还是好考虑的 f[i][j]表示前i个质数有j的约数最小数字 f[i][j]=minprime[i]k|jf[i?1][j/(k+1)]?prime[i]k 但万恶的高精度啊= =,于是改成搜索,然后用对数来表示,就好了 bzoj 2440 一道很神的数论题,二分+容斥+莫比

Codeforces Round #302 (Div. 2) (ABCD题解)

比赛链接:http://codeforces.com/contest/544 A. Set of Strings time limit per test:1 second memory limit per test:256 megabytes You are given a string q. A sequence of k strings s1,?s2,?...,?sk is called beautiful, if the concatenation of these strings is