//https://img2018.cnblogs.com/blog/1646268/201908/1646268-20190806114008215-138720377.jpg

NOIP 2021 数列

暴力

下标看起来真的乱,30min 打完暴力:

/*
 * @Author: Aisaka_Taiga
 * @Date: 2023-10-27 15:32:25
 * @LastEditTime: 2023-10-27 16:08:12
 * @LastEditors: Aisaka_Taiga
 * @FilePath: \Desktop\P7961.cpp
 * The heart is higher than the sky, and life is thinner than paper.
 */
#include <bits/stdc++.h>

#define int long long
#define P 998244353
#define N 1000100

using namespace std;

inline int read()
{
    int x = 0, f = 1;
    char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') f = -1; c = getchar();}
    while(c <= '9' && c >= '0') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return x * f;
}

int n, m, k, a[N], b[N], ans;

inline int ksm(int x, int y)
{
    int res = 1;
    while(y)
    {
        if(y & 1) res *= x;
        x *= x, y >>= 1;
    }
    return res;
}

inline void check(int xx)
{
    int cnt = 0, res = 1;
    while(xx)
    {
        if(xx & 1) cnt ++;
        xx >>= 1;
    }
    // for(int i = 1; i <= n; i ++)
    //     cout << b[i] << " ";
    // cout << endl;
    if(cnt > k) return ;
    for(int i = 1; i <= n; i ++)
        res = (res * a[b[i]]) % P;
    ans = (ans + res) % P;
    return ;
}

inline void dfs(int sum, int cnt)
{
    if(cnt == n + 1) return check(sum), void();
    for(int i = 0; i <= m; i ++)
    {
        b[cnt] = i;
        dfs(sum + ksm(2, i), cnt + 1);
        // b[cnt] = 0;
    }
    return ;
}

signed main()
{
    n = read(), m = read(), k = read();
    for(int i = 0; i <= m; i ++) a[i] = read();
    dfs(0, 1);
    cout << ans << endl;
    return 0;
}
/*
8 9 1
499488183 118995914 332316574 399234563 246850128 338768262 489466833 673193710 723933572 12759125
*/

由于太过暴力所以导致他在 O2 以及快速幂的加持下才拿到了 5pts。

正解

考虑搜索最终每一位上的数选多少个,然后根据组合数计算价值和。

那么我们直接搜肯定过不了,要记忆化,设 \(f[x][y][a][b]\) 表示当前枚举到 \(x\),填了 \(y\) 个数,处理完的位上的 \(1\) 的个数为 \(a\),前面进位对当前位的贡献为 \(b\)

接下来处理答案的计算以及下一个搜索的状态。

设当前位选了 \(x\) 个,那么这次搜索回来的值要乘上 \(a[i]^x \times C_{n-y}^{x}\), 前面那部分就是取 \(x\) 个对答案的贡献,后面就是从剩下的 \(x-y\) 个位置里选 \(x\) 个填上当前枚举的数的方案数。

而对于 \(a\) 的计算,只考虑当前位是不是为 \(1\) 即可,也就是下一层搜索的 \(a\) 的状态更新为 \((a+b)\bmod 2\),加 \(b\) 是因为前面的进位会影响这里是不是 \(1\);对于 \(b\),直接除以 \(2\) 即可。

那么最后得出转移方程:

\[f[x][y][a][b] = \sum_{i = 0}^{n - y} f[x + 1][y + i][(a + b) \& 1][\left \lfloor \frac{a + b}{2} \right \rfloor ] \times v_{x}^{i} \times C_{n-y}^{i} \]

/*
 * @Author: Aisaka_Taiga
 * @Date: 2023-10-27 15:32:25
 * @LastEditTime: 2023-10-27 17:11:25
 * @LastEditors: Aisaka_Taiga
 * @FilePath: \Desktop\P7961.cpp
 * The heart is higher than the sky, and life is thinner than paper.
 */
#include <bits/stdc++.h>

#define int long long
#define P 998244353
#define N 110
#define M 35

using namespace std;

inline int read()
{
    int x = 0, f = 1;
    char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') f = -1; c = getchar();}
    while(c <= '9' && c >= '0') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return x * f;
}

int n, m, k, ans, a[N], c[M][M], p[N][M], f[N][M][M][M];

inline int popcnt(int x)//??计算一个数转成二进制后一的个数
{
    int res = 0;
    while(x)
    {
        if(x & 1) res ++;
        x >>= 1;
    }
    return res;
}

inline int dfs(int x, int y, int a, int b)//x是当前a的值,y是当前选了的个数
{//a是当前已经处理完的位上的1的个数,b是进位后当前位相当于有多少个2^x
    if(y >= n) return a + popcnt(b) <= k;//a是前几个位的1个数,popcnt b是进位产生的1的个数
    if(x > m) return 0;//越界了,没有合法方案
    if(f[x][y][a][b] != -1) return f[x][y][a][b];//记忆化
    int res = 0;//当前状态下的答案
    for(int i = 0; i <= n - y; i ++)//一个不选~最多能选的个数
        res = (res + dfs(x + 1, y + i, a + ((b + i) & 1), ((b + i) >> 1)) * p[x][i] % P * c[n - y][i] % P) % P;
    return f[x][y][a][b] = res;//记忆化
}

signed main()
{
    memset(f, -1, sizeof f);
    n = read(), m = read(), k = read();
    for(int i = 0; i <= m; i ++) a[i] = read();
    for(int i = 1; i <= n; i ++)
    {
        c[i][0] = c[i][i] = 1;
        for(int j = 1; j < i; j ++)
            c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % P;
    }//预处理组合数
    for(int i = 0; i <= m; i ++)
    {
        p[i][0] = 1;
        for(int j = 1; j <= n; j ++)
            p[i][j] = p[i][j - 1] * a[i] % P;
    }//预处理a[i]^j
    ans = dfs(0, 0, 0, 0);
    cout << ans << endl;
    return 0;
}
/*
8 9 1
499488183 118995914 332316574 399234563 246850128 338768262 489466833 673193710 723933572 12759125

5 1 1
2 1
*/
posted @ 2023-10-27 17:24  北烛青澜  阅读(7)  评论(0编辑  收藏  举报