【BZOJ2339】卡农(递推,容斥)

题面

BZOJ

题解

先简化一下题意:
[1,2n1] [ 1 , 2 n − 1 ] 中选择不重复的 m m 个数,使得他们异或和为0的方案数。

我们设 f[i] f [ i ] 表示选择 i i 个数异或和为0的方案数。
直接算是很麻烦的,所以我们反过来,总数减去不合法的。
因为确定了前 i1 i − 1 个数最后一个数就已经知道了。
所以总方案数是 Ai12n1 A 2 n − 1 i − 1 ,不合法的有两种,一种是选择了 0 0 ,一种是有重复。
选择了0,意味着前 i1 i − 1 个数的异或和为 0 0 ,所以方案数是f[i1]种。
有重复,我们枚举哪个数重复了,那么剩下的 i2 i − 2 个数的异或和仍然为 0 0
所以方案数是f[i2]×(2n1(i2)),题目没有考虑顺序,但是我们计算的时候先考虑了顺序,所以这里的方案数还需要考虑在哪个位置,也就是再乘上一个 (i1) ( i − 1 )
所以

f[i]=Ai12n1f[i1](i1)×f[i2]×(2n1(i2)) f [ i ] = A 2 n − 1 i − 1 − f [ i − 1 ] − ( i − 1 ) × f [ i − 2 ] × ( 2 n − 1 − ( i − 2 ) )

最终的答案再把顺序的问题处理一下就好了。

#include<iostream>
#include<cstdio>
using namespace std;
#define MOD 100000007
#define MAX 1000100
inline int read()
{
    int x=0,t=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int jv=1,A[MAX],n,m,p,f[MAX];
int fpow(int a,int b)
{
    int s=1;
    while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
    return s;
}
int main()
{
    n=read();m=read();p=fpow(2,n);f[0]=A[0]=1;f[1]=0;
    for(int i=1;i<=m;++i)jv=1ll*jv*i%MOD;jv=fpow(jv,MOD-2);
    for(int i=1;i<=m;++i)A[i]=1ll*A[i-1]*(p-i+MOD)%MOD;
    for(int i=2;i<=m;++i)f[i]=((A[i-1]-f[i-1]+MOD)%MOD-1ll*f[i-2]*(i-1)%MOD*(p-1-(i-2)+MOD)%MOD+MOD)%MOD;
    printf("%lld\n",1ll*jv*f[m]%MOD);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值