【学习笔记】CF643F Bears and Juice

完了,感觉一上午做不出一道题来了。

大脑超载了。无论如何都只想到了从 i = 1 i=1 i=1入手这一种思路。手玩了一下发现答案就是 n + 1 n+1 n+1

然后考虑 i = 2 i=2 i=2。(真的只能想到最笨的思路啊)发现就按 n + 1 n+1 n+1的大小分组即可,但是注意少了一个所以应该是按 n n n大小为分组,那么答案就是 n 2 + 1 n^2+1 n2+1。方便起见先不考虑 p p p的限制。

我们尝试来推一下通式。经验告诉我们这种题目都是有通式的,只是比较隐晦而已。这个通式算出来是 n i + 1 n^i+1 ni+1,看着就不是很对。

回过头去看样例,原来 p p p有限制,那么自然而然的想到设计一个两维的 D P DP DP状态,设 d p i , j dp_{i,j} dpi,j表示有 i i i天, j j j张床时最多的酒桶数目。唯一需要注意的是此时熊的数目是可以确定的,但是这个递推的式子需要仔细斟酌一下,仔细观察一下样例 3 3 3会发现问题没有那么简单,换句话说一轮少掉的熊的数目可能不只 1 1 1个,因此要换一个角度来转移。

我们之前接触过全集的剖分这个概念,用来解释这个转移是非常合适的。考虑我们得到的信息是哪些熊少掉了,一共有 U = 2 n − p + j U=2^{n-p+j} U=2np+j种情况,对于每种情况能区分出来的桶的最大数量求和,就得到了正确的转移式: d p i , j = ∑ k ≤ j ( n − p + j k ) d p i − 1 , j − k dp_{i,j}=\sum_{k\le j}\binom{n-p+j}{k}dp_{i-1,j-k} dpi,j=kj(knp+j)dpi1,jk。注意这取的是上界,可以证明我们能构造方案将这个上界取到(?)。

注意这里要处理一下边界,不妨令 p = min ⁡ ( p , n − 1 ) p=\min(p,n-1) p=min(p,n1),这样就不用考虑熊全部死光的情况了。直接拿这个式子去转移,复杂度 O ( p 2 q ) O(p^2q) O(p2q),难泵。

但是注意到这个 d p dp dp状态是二维的,很难不让人想到组合数。再瞪眼一看这个转移系数,这不就是 E G F EGF EGF吗?简单来说,要算 d p i , p dp_{i,p} dpi,p的值,将 e x i e^{xi} exi泰勒展开,有 e x i = 1 + x i + x 2 i 2 2 ! + . . . + x n i n n ! e^{xi}=1+xi+\frac{x^2i^2}{2!}+...+\frac{x^ni^n}{n!} exi=1+xi+2!x2i2+...+n!xnin,把系数拼凑一下得到 d p i , p = ∑ j ≥ n − p n ! j ! [ x n − j ] e x i = ∑ j ≤ p ( n j ) i j dp_{i,p}=\sum_{j\ge n-p}\frac{n!}{j!}[x^{n-j}]e^{xi}=\sum_{j\le p}\binom{n}{j}i^{j} dpi,p=jnpj!n![xnj]exi=jp(jn)ij。这里唯一需要提醒的就是求和的范围,非常容易被忽略。

上式可以直接 O ( p q ) O(pq) O(pq)计算。唯一恼人的是这个组合数,因为 2 32 2^{32} 232不是质数,不能直接算。但是把 2 2 2的因子全部约掉然后用欧拉定理就好了,代码并不困难。

#include<bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define db double #define inf 0x3f3f3f3f3f3f3f3f #define uint unsigned int using namespace std; const int mod=1e9+7; int n,p,q,nums[155]; uint res,tran[155]; uint fpow(uint x,ll y=(1ll<<31)-1){ uint z(1); for(;y;y>>=1){ if(y&1)z=z*x; x=x*x; } return z; } signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>p>>q,p=min(p,n-1); tran[0]=1; for(int i=1;i<=p;i++){ int x=n-i+1,y=i;nums[i]=nums[i-1]; while(x%2==0)x/=2,nums[i]++; while(y%2==0)y/=2,nums[i]--; assert(nums[i]>=0); tran[i]=tran[i-1]*x*fpow(y); } for(int i=1;i<=p;i++){ tran[i]=tran[i]*(1ll<<nums[i]); } for(int i=1;i<=q;i++){ uint tmp=0,mul=1; for(int j=0;j<=p;j++){ tmp+=mul*tran[j]; mul=mul*i; } res^=(tmp*i); } cout<<res; }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17529950.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(8)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2022-06-24 【学习笔记】模拟
点击右上角即可分享
微信分享提示