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;
}
全部评论

相关推荐

不愿透露姓名的神秘牛友
07-07 18:05
哈哈哈哈哈感觉朋友找工作的已经疯掉了,直接上图
码农索隆:真老板娘:“我嘞个去,这不我当年的套路吗
点赞 评论 收藏
分享
Twilight_m...:表格简历有点难绷。说说个人看法: 1.个人基本情况里好多无意义信息,什么婚姻状况、健康状况、兴趣爱好、户口所在地、身份证号码、邮政编码,不知道的以为你填什么申请表呢。 2.校内实践个人认为对找工作几乎没帮助,建议换成和测开有关的项目,实在没得写留着也行。 3.工作经历完全看不出来是干什么的,起码看着和计算机没啥关系,建议加强描述,写点你在工作期间的实际产出、解决了什么问题。 4.个人简述大而空,看着像AI生成,感觉问题最大。“Python,C,C++成为我打造高效稳定服务的得力工具”、“我渴望凭借自身技术知识与创新能力,推动人工智能技术的应用发展,助力社会实现智能化转型”有种小学作文的美感。而且你确定你个人简述里写的你都会嘛?你AI这块写的什么“深入研究”,发几篇顶会的硕博生都不一定敢这么写。而且你AI这块的能力和软测也完全无关啊。个人简述建议写你对哪些技术栈、哪些语言、哪些生产工具的掌握,写的有条理些,而且最好是和测开强相关的。
点赞 评论 收藏
分享
那一天的Java_J...:他本来公司就是做这个的,不就是正常的游戏客户端和服务器开发,软硬件联动,有啥恶心不恶心的,提前告诉你就是怕你接受不了,接受不了就没必要再往后走流程浪费时间,虽然这公司是一坨。
点赞 评论 收藏
分享
来个大佬救一下,为上投了都是石沉大海了,没实习经历的话怕秋招直接进不了面。什么实习这么难找,基本
心态爆炸了:现在正式的岗位都少,实习基本不咋招的,除了大厂,中小企业其实没那么多岗位需求,就算是有,大多都是招一两个廉价劳动力,同时,他们也会希望你一来就能干活的,没时间培训你,就让你了解公司的项目,你了解完就可以开始干活。再者是,很多低质量的实习其实用处没有那么大的。我去年也是找实习找到破防,最后去了一家深圳的小公司实习,工作对我来说很简单,甚至不如我在学校做的项目,秋招的时候,这段实习经历也并没有帮上什么忙,投递简历,依旧非常低的回复率。低回复率是常态,尤其是找实习,找不到,那就把重心放在优化自己的简历和项目,多看八股文,锻炼自己的面试能力,多看别人的面经,自己模拟面试,等秋招的时候,只要有那么寥寥几次,好好抓住那几次机会。
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

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