[题解] LOJ6039 小 Y 的背包计数

题面

题解

我 TM 以为是一道多项式题

考虑到这样一个性质

对于每个编号大于 \(\sqrt{n}\) 的物品, 我们不会选超过 \(\sqrt {n}\)

所以我们把整个选择分为两部分来看

第一部分是编号小于等于 \(\sqrt{n}\)

我们设 \(f[i][j]\) 代表选了编号前 \(i\) 个的, 体积总和为 \(j\) 的方案数

前缀和优化一下就行

第二部分是编号大于 \(\sqrt{n}\)

我们可以将选择变成这样两种操作

  • 把选择的 \(i\) 个数全部加一
  • 加入一个大小为 \(\sqrt {n} + 1\) 的数

可以证明这两个操作可以把编号大于 \(\sqrt{n} + 1\) 的选择方案全部不重不漏的表达出来

没了, 两个乘起来就行了

扫描二维码关注公众号,回复: 8545170 查看本文章

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
const int mod = 23333333;
const int N = 1e5 + 5; 
using namespace std;

int n, m, f[405][N], g[405][N], s[N], ans; 

template < typename T >
inline T read()
{
    T x = 0, w = 1; char c = getchar();
    while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * w; 
}

int main()
{
    n = read <int> (), m = sqrt(n);
    for(int i = 0; i <= m; i++)
        f[i][0] = 1; 
    for(int i = 1; i <= m; i++)
    {
        for(int j = 0; j <= n; j++)
            s[j] = ((j - i >= 0 ? s[j - i] : 0) + f[i - 1][j]) % mod; 
        for(int j = 0; j <= n; j++)
            f[i][j] = (s[j] - (j - i * i - i >= 0 ? s[j - i * i - i] : 0) + mod) % mod; 
    }
    memset(s, 0, sizeof(s)), g[0][0] = 1; 
    for(int i = 1; i <= m; i++)
        for(int j = 0; j <= n; j++)
        {
            if(j - i > 0) g[i][j] = g[i][j - i]; 
            if(j - m > 0) g[i][j] = (g[i][j] + g[i - 1][j - m - 1]) % mod; 
            s[j] = (s[j] + g[i][j]) % mod; 
        }
    for(int i = 0; i <= n; i++)
        ans = (1ll * ans + 1ll * s[n - i] * f[m][i] % mod) % mod;
    ans = (1ll * ans + f[m][n]) % mod; 
    printf("%d\n", ans); 
    return 0; 
}

猜你喜欢

转载自www.cnblogs.com/ztlztl/p/12182440.html
今日推荐