嘘~ 正在从服务器偷取页面 . . .

洛谷P7961 [NOIP2021] 数列 题解


洛谷P7961 [NOIP2021] 数列 题解

题目链接:P7961 [NOIP2021] 数列

题意

给定整数 $n, m,d$,和一个长度为 $m + 1$ 的正整数数组 $v_0, v_1, \ldots, v_m$。

对于一个长度为 $n$,下标从 $1$ 开始且每个元素均不超过 $m$ 的非负整数序列 $\{a_i\}$,我们定义它的权值为 $v_{a_1} \times v_{a_2} \times \cdots \times v_{a_n}$。

当这样的序列 $\{a_i\}$ 满足整数 $S = 2^{a_1} + 2^{a_2} + \cdots + 2^{a_n}$ 的二进制表示中 $1$ 的个数不超过 $d$ 时,我们认为 $\{a_i\}$ 是一个合法序列。

计算所有合法序列 $\{a_i\}$ 的权值和对 $998244353$ 取模的结果。

输入格式

输入第一行是三个整数 $n, m, d$。

第二行 $m + 1$ 个整数,分别是 $v_0, v_1, \ldots, v_m$。

输出格式

仅一行一个整数,表示所有合法序列的权值和对 $998244353$ 取模的结果。

数据范围

对所有测试点保证 $1 \leq d \leq n \leq 30$,$0 \leq m \leq 100$,$1 \leq v_i < 998244353$。

果然是 NOIP 的dp题,这个dp就是很怪。

注意到这个题目最麻烦的就是这个 $S$ 会进位。

进位有什么性质呢,好像除了从低到高进位,没啥特殊的性质。但是这已经够dp了

设 $f(i,j,k,p)$ 表示讨论了 $S$ 的前 $i~(1\le i \le m+1)$ 位,已经确定了 $j$ 个 $a$ 中的元素,$S$ 从低到高前 $i$ 为中有 $k$ 个 $1$ ,要向当前讨论位(即 $i+1$ )的进位为 $p$ 时的方案数。边界 $f(0,0,0,0) = 1$ 。

显然考虑刷表法转移。我们枚举序列 $a$ 中有 $t$ 个 $i$ ,则对 $S$ 第 $i$ 位的贡献了 $t$ 个 $1$ 。

而进位还有 $p$ ,所以对 $i+1$ 位的进位为 $\left\lfloor\frac{t+p}{2}\right\rfloor$ ,则转移为

最后答案为(其中 $\mathrm{popc}$ 表示种群计数函数 __builtin_popcount()

因为 $S$ 有 $m+1$ 位,并且共有 $n$ 个 $a$ ,所以第一位为 $m+1$ ,第二位为 $n$ 。

时间复杂度 $\mathcal{O}(n^4m)$

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define popc(a) __builtin_popcountll(a)
#define rep(i,a,b) for(int (i)=(a); (i)<=(b); (i)++)
const int mod = 998244353;
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
void add(int &x,int y) { (x += y) >= mod ? x -= mod : 0; }
#define mul3(a,b,c) ((a) % mod * (b) % mod * (c) % mod)
#define N ((int)(35))
#define M ((int)(105))

int n,m,d,res,v[M],C[N][N],f[M][N][N][16], pv[M][N];
void init(int n)
{
    for(int i=0; i<=n; i++) C[i][0] = 1;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=i; j++)
            C[i][j] = (C[i-1][j] + C[i-1][j-1]) % mod;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m >> d; init(N-5);
    for(int i=0; i<=m; i++)
    {
        cin >> v[i]; pv[i][0] = 1;
        for(int j=1; j<=n; j++)
            pv[i][j] = pv[i][j-1] * v[i] % mod;
    }
    f[0][0][0][0] = 1;
    rep(i,0,m) rep(j,0,n) rep(k,0,d) rep(p,0,n/2) rep(t,0,n-j)
    {
        add(f[i + 1][j + t][k + ((t+p) & 1)][(t+p) / 2],
        mul3(f[i][j][k][p], pv[i][t], C[n-j][t]));
    }
    for(int k=0; k<=d; k++)
        for(int p=0; p<=n/2; p++)
            if(k + popc(p) <= d) add(res, f[m+1][n][k][p]);
    cout << res << '\n';
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/blog/Sham-Devour/solution-p7961


题外话

今日乐子,一个人的网课(肚子不舒服请假了

不要吐槽这个分辨率,因为 4k 的外接现在在用于写博客(恼


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录