把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CF643F】Bears and Juice(信息与可区分情况数)

点此看题面

  • \(n\)头熊和若干桶果汁以及一桶酒。
  • 每天每只熊会选择若干桶饮料喝一杯,如果喝到了酒就会去睡觉再也不回来。
  • 总共只有\(p\)个睡觉的位置,如果睡觉的熊超过了\(p\)头或者所有熊都睡觉了就失败了。
  • \(f_i\)表示\(i\)天内在这些熊能找出酒的位置的前提下的最大桶数,求\(\oplus_{i=1}^q(i\times f_i)(mod\ 2^{32})\)
  • \(n\le10^9,p\le130,q\le2\times10^6\)

“信息”

非常巧妙的一个转化!

我们可以先通过一个例子来感受一下“信息”与可区分情况数之间的联系。

例如排序,为什么一般的基于比较的排序复杂度最快只能达到\(O(nlog_2n)\)

考虑通过一次比较,我们获得的信息能够区分出两种不同的情况,因此\(k\)次比较总共就能区分出\(2^k\)种情况。

而排序要求的是把序列从总共的\(n!\)种情况中区分出来,就需要满足\(2^k\ge n!\)

因此\(k\ge log_2(n!)≈nlog_2n\)

可区分情况数

在这题中,由于有多少个桶,酒所在的位置就有多少种情况,因此问最多能有多少桶,其实就是问我们通过\(i\)天操作得到的信息最多能区分出多少种不同的情况。

首先,由于不能所有熊都睡着,因此当\(p>n-1\)时多余的位置其实是没意义的,因此我们完全可以在一开始就令\(p=\min\{p,n-1\}\)

然后,考虑有多少种可区分情况,先枚举有多少头熊睡觉了,再考虑睡觉的是哪些熊、分别在哪一天睡着,得到:

\[f_i=\sum_{j=0}^{p}C_n^j\times i^j \]

要注意,这道题之所以可以这样算,是因为不同的信息必然可以对应出一种不同的可区分情况,不能盲目把这个方法套用到所有题目上去。

对于组合数的预处理

上面这个式子显然可以\(O(pq)\)计算,唯一的问题就在于如何在模\(2^{32}\)意义下求组合数,这应该是这道题另一个比较妙的地方,尽管与前面那位相比逊色不少。

发现\(p\)很小,我们直接列出所有的分子和分母,枚举每对分子和分母约分,根据组合数的定义最终必然能把所有分母约成\(1\),那么再直接把所有分子乘起来即可得出组合数了。

代码:\(O(pq+p^3logp)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define P 130
#define LL long long
using namespace std;
int n,p,q,a[P+5],b[P+5];unsigned C[P+5];
I int gcd(CI x,CI y) {return y?gcd(y,x%y):x;}
int main()
{
	RI i,j,k,g;for(scanf("%d%d%d",&n,&p,&q),p=min(p,n-1),i=0;i<=p;++i)//求C(n,i)
	{
		for(j=1;j<=i;++j) a[j]=n-j+1,b[j]=j;//列出所有分子和分母
		for(j=1;j<=i;++j) for(k=1;k<=i;++k) g=gcd(a[j],b[k]),a[j]/=g,b[k]/=g;//枚举每对分子和分母约分
		for(C[i]=j=1;j<=i;++j) C[i]*=a[j];//将所有分子乘起来
	}
	unsigned w,t,s=0;for(i=1;i<=q;s^=i*t,++i) for(w=1,t=j=0;j<=p;w*=i,++j) t+=C[j]*w;//枚举每一天计算可区分情况数
	return printf("%u\n",s),0;
}
posted @ 2021-04-02 15:28  TheLostWeak  阅读(96)  评论(0编辑  收藏  举报