2022 年牛客多校第十场 G 题题解

Steins;Game 2

https://ac.nowcoder.com/acm/contest/33195/G

G Steins’ Game 2

题意:有 nn 堆石子 {an}\{a_n\},满足 0a1a2anm0 \leq a_1 \leq a_2\cdots \leq a_n \leq m。Alice 与 Bob 依次从非空的一堆拿走正数个石子使得每堆石子的石子个数仍然是非递减的。求有多少种合法的石子分配方案使得 Bob 必胜。n4×104n\leq 4\times 10^4m1×1012m \leq 1\times 10^{12}

解法:考虑 {an}\{a_n\} 的差分数组 {bn}\{b_n\},则该问题与 {bn}\{b_n\} 序列构成的阶梯 Nim(有 nn 堆石子,每次可以从第 ii 堆的石子中拿走一部分放到第 i1i-1 堆中,或者把第 11 堆中的石子拿走一部分,无法操作者算输)是完全等价的。考虑 Bob 获胜条件,利用阶梯 Nim 的必胜条件:奇数堆石子的异或值为 00。得到这题的转化题意:前 k=n2k=\left \lceil \dfrac{n}{2}\right \rceil 个石子异或值为 00bi=m\sum b_i=m 的方案数。这是因为如果有奇数堆石子,则最后新添加一堆石子不会影响阶梯 Nim 的性质,这时不妨在 {an}\{a_n\} 的末尾添加一堆石子数为 mm 的石子;若原来有偶数堆石子,也可以在 {an}\{a_n\} 的最后增补两堆均为 mm 的石子,此时对于 {bn}\{b_n\} 序列只是增加了一个 00。则该转化将 {bn}\{b_n\} 中奇数堆石子移动到最前面,变成前缀异或和,且忽略了 {bn}\{b_n\} 中每个数字的大小关系。

考虑每一个数字的每一个 bit 位上填 0,10,1 的方案。对于 2k2^k 这一 bit,要求前缀 kk 的异或和为 00 的的生成函数为 f(x)=i=0k[imod2=0](ki)xif(x)=\displaystyle \sum_{i=0}^{k}[i \bmod 2=0]{k \choose i}x^i,即仅能选择偶数个数字填 11,并且进行选择。对于剩余的 nkn-k 个数字,可以任选,其方案为 g(x)=(1+x)nkg(x)=(1+x)^{n-k},因而整层的填法方案的生成函数为 h(x)=f(x)g(x)h(x)=f(x)g(x)。不难发现,对于每一层的填法均相同,因而可以预处理。

接下来考虑背包的容量。这一处理与 2022 年牛客第一场的 H 题是完全一样的:fi,jf_{i,j} 表示填完低 ii 个 bit,最后剩余 j2ij2^i 的容量的方案数。则转移是显然的:fi+1,j=k=0lfi,k[xlk]h(x)\displaystyle f_{i+1,j}=\sum_{k=0}^{l} f_{i,k}[x^{l-k}]h(x),其中 l=2j+bitil=2j+{\rm bit}_i 表示当前位的最大容量。注意到这个 j2nj \leq 2n(更大了则完全填不上),因而只需要保留 2n2n 项即可。

总时间复杂度 O(nlognlogm)\mathcal O(n \log n \log m)

int main()
{
    int n;
    long long m;
    scanf("%d%lld", &n, &m);
    Poly a, b;
    if (n % 2 == 0)
    {
        int k = n / 2 + 1;
        b.resize(k + 1);
        a.resize(n / 2 + 1);
        for (int i = 0; i <= k; i++)
            b[i] = C(k, i);
        for (int i = 0; i <= n / 2; i += 2)
            a[i] = C(n / 2, i);
    }
    else
    {
        int k = (n + 1) / 2;
        b.resize(k + 1), a.resize(k + 1);
        for (int i = 0; i <= k; i++)
            b[i] = C(k, i);
        for (int i = 0; i <= k; i += 2)
            if (i % 2 == 0)
                a[i] = C(k, i);
    }
    auto way = a * b;
    vector<int> digit;
    long long x = m;
    while (x)
    {
        digit.push_back(x & 1);
        x >>= 1;
    }
    f[0][0] = 1;
    for (int k = 1; k <= digit.size(); k++)
    {
        Poly cur(2 * n + 5);
        for (int i = 0; i <= n + 1;i++)
            cur[i] = f[k - 1][i];
        cur = cur * way;
        for (int i = 0; i <= n + 1;i++)
            f[k][i] = cur[i * 2 + digit[k - 1]];
    }
    printf("%lld", f[digit.size()][0]);
    return 0;
}
全部评论

相关推荐

来,说点可能被同行“骂”的大实话。🙊当初接数字马力Offer时,朋友都说:“蚂蚁的“内包”公司?你想清楚啊!”但入职快一年后的今天,我反而对他有了不一样的看法!🔹&nbsp;是偏见?还是信息差!之前没入职之前外面都在说什么岗位低人一等这类。实际上:这种情况不可至否,不能保证每个团队都是其乐融融。但我在的部门以及我了解的周边同事都还是十分好相处的~和蚂蚁师兄师姐之间也经常开一些小玩笑。总之:身份是蚂蚁公司给的,地位是自己挣的(一个傲娇女孩的自述)。🔹&nbsp;待遇?玩的就是真实!试用期工资全额发!六点下班跑得快(早9晚6或者早10晚7,动态打卡),公积金顶格交。别听那些画饼的,到手的钱和下班的时间才是真的(都是牛马何必难为牛马)。🔹&nbsp;能不能学到技术?来了就“后悔”!我们拥有权限直通蚂蚁知识库,技术栈多到学不完。说“学不到东西”的人,来了可能后悔——后悔来晚了(哈哈哈哈,可以不学但是不能没有)!💥&nbsp;内推地址:https://app.mokahr.com/su/ueoyhg❗我的内推码:NTA6Nvs走我的内推,可以直达业务部门,面试流程更快速,进度可查!今天新放HC,之前挂过也能再战!秋招已经正式开始啦~机会就摆在这,敢不敢来试一试呢?(和我一样,做个勇敢的女孩)
下午吃泡馍:数字马力的薪资一般哇,5年经验的java/测试就给人一万出头,而且刚入职第三天就让人出差,而且是出半年
帮你内推|数字马力 校招
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务