完了,感觉一上午做不出一道题来了。
大脑超载了。无论如何都只想到了从 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=2n−p+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=∑k≤j(kn−p+j)dpi−1,j−k。注意这取的是上界,可以证明我们能构造方案将这个上界取到(?)。
注意这里要处理一下边界,不妨令 p = min ( p , n − 1 ) p=\min(p,n-1) p=min(p,n−1),这样就不用考虑熊全部死光的情况了。直接拿这个式子去转移,复杂度 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=∑j≥n−pj!n![xn−j]exi=∑j≤p(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;
}