luogu P3214 [HNOI2011] 卡农
首先我们转化一下题意:有\([1,2^n-1]\)中选择\(m\)个数,满足异或起来为\(0\)的方案数。
容易想到,我们前\(m-1\)个数可以任意选,只要最后一个数等于前面所有数的异或和即可满足全部为\(0\)。
但是这样的构造方案有三个问题:最后一个数可能为\(0\),最后一个数可能与前面的数重复,以及一种方案会被重复计算\(m\)次。
最后一个问题是容易解决的,排除掉不可能的方案后除以\(m\)即可。第一个也是平凡的,最后一个数为\(0\)说明前\(m-1\)个数已经满足条件,是一个子问题。而第二个问题说明排除掉这两个重复的数,其它数也构成了一个子问题,只要再选择两个没有出现过的重复的数即可。具体的,设\(f_i\)为\(m=i\)时候的答案,则有递推式\(f_i=\frac{C_{2^n-1}^{i}-f_{i-1}-f_{i-2}\times (2^n-i+1)}{i}\)。
组合数可以用卢卡斯定理计算,时间复杂度\(O(m\log p)\)
code:
#include<bits/stdc++.h>
#define I inline
#define ll long long
#define db double
#define lb long db
#define N (1000000+5)
#define M ((N<<2)+5)
#define K (1500+5)
#define mod 100000007
#define Mod (mod-1)
#define eps (1e-5)
#define ull unsigned ll
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((k+1)*(x)+(y))
#define R(n) (1ll*rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int n,m,k,x,y,z;ll C[N],F[N];
I ll mpow(ll x,int y=mod-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%mod),y>>=1,x=x*x%mod;return Ans;}
int main(){
freopen("1.in","r",stdin);
int i,j;scanf("%d%d",&n,&m);k=mpow(2,n)-1;for(C[0]=i=1;i<=min(m,k);i++) C[i]=C[i-1]*(k-i+1)%mod*mpow(i)%mod;
F[1]=F[2]=0;for(i=3;i<=m;i++) F[i]=(C[i-1]-F[i-1]-F[i-2]*(k-(i-2))%mod)*mpow(i)%mod;printf("%lld\n",(F[m]+mod)%mod);
}

浙公网安备 33010602011771号