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

【洛谷3214】[HNOI2011] 卡农(思维)

点此看题面

  • 问有多少种大小为\(m\)、值域为\([1,2^n)\)的无序且无重复元素的集合,满足其中所有数异或和为\(0\)
  • \(n,m\le10^6\)

巧妙的计数思想

首先,由于集合中元素不重复,方便起见我们可以改成求有序集合数,只要最后给答案除以一个\(m!\)即可。

\(f_i\)表示大小为\(i\)的集合个数。

考虑我们随意选择前\(i-1\)个数(方案数为\(A_{2^n-1}^{i-1}\)),由于异或和要为\(0\),那么第\(i\)位上的数就唯一确定了,但它并不一定满足题目中对集合的两个限制:

  1. 它不能为\(0\)
  2. 它不能与选中的数重复。

容易发现,因为选中的数中不可能存在\(0\),是没有同时违反这两个限制的情况的,故可以分别考虑。

如果它是\(0\),说明选中的这\(i-1\)个数异或和为\(0\)。这种情况的方案数正是\(f_{i-1}\)

如果它与某个选中的数重复(假设编号为\(j\)),那么除\(i,j\)以外的这\(i-2\)个数异或和也必须是\(0\)。这种情况方案数是\(f_{i-2}\),而\(j\)的选择有\(i-1\)种,对于确定的\(i-2\)个数\(i\)的取值有\((2^n-1)-(i-2)\)种。

因此,得到最终的计算式:

\[f_i=A_{2^n-1}^{i-1}-f_{i-1}-f_{i-2}\times (i-1)\times((2^n-1)-i+2) \]

注意,其中的排列下标都是\(2^n-1\),因此可以线性预处理。

代码:\(O(logn+m)\)

#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 N 1000000
#define X 100000007
using namespace std;
int n,m,A[N+5],f[N+5];
I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
int main()
{
	RI i,p;for(scanf("%d%d",&n,&m),p=QP(2,n)-1,A[0]=i=1;i<=m;++i) A[i]=1LL*A[i-1]*(p-i+1)%X;//预处理排列
	for(f[0]=1,f[1]=0,i=2;i<=m;++i) f[i]=(0LL+A[i-1]-f[i-1]-1LL*f[i-2]*(i-1)%X*(p-i+2)%X+2*X)%X;//递推
	RI t=1;for(i=1;i<=m;++i) t=1LL*t*i%X;return printf("%d\n",1LL*f[m]*QP(t,X-2)%X),0;//有序化无序
}
posted @ 2021-01-22 20:55  TheLostWeak  阅读(112)  评论(0编辑  收藏  举报