链接
http://www.lydsy.com/JudgeOnline/problem.php?id=2339
题解
是道好题,真心舒爽。
把它转化一下稍微好想一些,就是说你要从
[1,2n−1]
[
1
,
2
n
−
1
]
这么多二进制数里选择不同的
m
m
个,使得他们异或等于0。
好,设计状态,f[i]表示从这么多二进制数里选了i个使得异或等于0的方案数。
首先题目要求不考虑顺序,这个很烦,但是你知道一共就选择个,所以我们可以先算考虑顺序的,最后除以
m!
m
!
那么考虑计算f[i],首先只要前
i−1
i
−
1
个元素已经选好了,这个就被确定了,所以答案先加上
Ai−12n−1
A
2
n
−
1
i
−
1
,现在减去那些不合法的方案,如果当前这个数等于0的话是不合法的,对应的就是前面的异或值等于0;再就是当前的数和前面的某一个数重了。这个算起来有些不好想,先枚举和哪个数重了,一共(i-1)个,再排好剩下的那i-2个,即f[i-2],再枚举这个重了的数字是几,显然共有
2n−1−(i−2)
2
n
−
1
−
(
i
−
2
)
种。因此这类方案数等于
(i−1)∗f[i−2]∗(2n−1−(i−2))
(
i
−
1
)
∗
f
[
i
−
2
]
∗
(
2
n
−
1
−
(
i
−
2
)
)
。
令
tot=2n−1
t
o
t
=
2
n
−
1
,则最终的递推式
我自己肯定想不出来…
代码
//排列组合、动态规划
#include <cstdio>
#include <algorithm>
#define ll long long
#define mod 100000007
#define maxn 1000010
using namespace std;
ll a[maxn], f[maxn], N, M, tot, inv;
ll pow(ll a, ll b)
{
ll t, ans;
for(t=a,ans=1;b;b>>=1,t=t*t%mod)if(b&1)ans=ans*t%mod;
return ans;
}
void init()
{
ll i;
scanf("%lld%lld",&N,&M);
tot=pow(2,N)-1;
a[1]=tot;
for(i=2;i<=M;i++)a[i]=a[i-1]*(tot-i+1)%mod;
for(i=1,inv=1;i<=M;i++)inv=inv*i%mod;
inv=pow(inv,mod-2)%mod;
}
void dp()
{
for(ll i=3;i<=M;i++)f[i]=(a[i-1]-f[i-1]-f[i-2]*(tot-i+2)%mod*(i-1)%mod)%mod;
}
int main()
{
init();
dp();
printf("%lld",(f[M]+mod)%mod*inv%mod);
return 0;
}

本文探讨了一道关于从特定范围内选取不同二进制数,使这些数异或结果为0的问题。采用动态规划方法求解,并通过排列组合去除重复计数。代码实现包括初始化及动态规划两个阶段。
958

被折叠的 条评论
为什么被折叠?



