• LOJ #6089. 小 Y 的背包计数问题


    法老上课讲的题,感觉海星就写了下,比较常规的设阈值+DP

    首先我们考虑把物品分为两类,以(sqrt n)为阈值

    (ile sqrt n)时,显然现在物品总数很少,我们可以直接枚举每个物品然后做多重背包

    考虑使用多重背包的经典优化——完全背包差分,容易得出这部分的复杂度是(O(nsqrt n))

    然后当(i>sqrt n)时,显然此时每个物品最多拿(frac{n}{i})个,而(i>frac{n}{i}),因此物品个数可以看做无限制

    考虑用一个美妙的方法统计,设(f_{i,j})表示当前选了(i)个物品,体积和为(j)的方案数,转移操作有两种:

    • 新增一个物品,假定这个物品的体积为(sqrt n +1)
    • 将当前所有物品的体积都加(1)

    不难发现这样的每个方案都和一种选取方法唯一对应,因此是正确的,同时因为最多选(sqrt n)个物品,复杂度是(O(nsqrt n))

    综上,总体复杂度也是(O(nsqrt n))

    #include<cstdio>
    #include<cmath>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=100005,M=330,mod=23333333;
    int n,m,f1[N],f2[M][N],g[N],ans;
    inline void inc(int& x,CI y)
    {
    	if ((x+=y)>=mod) x-=mod;
    }
    inline void dec(int& x,CI y)
    {
    	if ((x-=y)<0) x+=mod;
    }
    int main()
    {
    	RI i,j; for (scanf("%d",&n),m=sqrt(n),f1[0]=i=1;i<=m;++i)
    	{
    		for (j=i;j<=n;++j) inc(f1[j],f1[j-i]);
    		for (j=n;j>=i*(i+1);--j) dec(f1[j],f1[j-i*(i+1)]);
    	}
    	for (f2[0][0]=1,i=0;i<=m;++i) for (j=0;j<=n;++j)
    	{
    		inc(g[j],f2[i][j]);
    		if (j+m+1<=n) inc(f2[i+1][j+m+1],f2[i][j]);
    		if (j+i<=n) inc(f2[i][j+i],f2[i][j]);
    	}
    	for (i=0;i<=n;++i) inc(ans,1LL*f1[i]*g[n-i]%mod);
    	return printf("%d",ans),0;
    }
    
  • 相关阅读:
    mysql +java中文乱码
    POJ1325二分匹配或者DINIC(最小路径覆盖)
    POJ1324贪吃蛇(状态压缩广搜)
    POJ1324贪吃蛇(状态压缩广搜)
    POJ1548最小路径覆盖
    POJ1548最小路径覆盖
    POJ1018贪心(多路归并的想法)
    POJ1018贪心(多路归并的想法)
    POJ2771最大独立集元素个数
    POJ2771最大独立集元素个数
  • 原文地址:https://www.cnblogs.com/cjjsb/p/13360068.html
Copyright © 2020-2023  润新知