BZOJ2339: [HNOI2011]卡农

Description
卡农
Input
Output
Sample Input
Sample Output
HINT
Source
Day2

首先题目里说是无序的,但是不要管它,我们先把它看成有序的,最后除以一个 m! 即可。我们考虑补集转换,首先所有的子集个数应该是 2n1 ,我们定义 f[i] 为挑选 i 个片段的合法的方案数,此时总数应该是A(2n1,i1)(排列数)。为什么是 i1 而不是 i 呢?因为要保证总数是偶数,也就是说如果你确定了i1个片段第 i 个片段也就确定了。而这样肯定多算了,具体来说有两部分:
1、如果前i1个已经合法,那么第 i 个就是空集,这样肯定不合法,所以要减去f[i1]
2、如果根据前 i1 个确定出来的第 i 个集合和前面的某一个重复,这样肯定是不合法的。
因为考虑顺序,所以那个和第i个重复的集合有 i1 种位置,对于每种位置,当前的总数偶数去掉两个数之后还是偶数,所以剩下其他数的方案数为 f[i2] 。然后我们需要算出有多少种可能重复的方案,因为我们已经确定了 (i2) 个位置,所以方案数为 (2n1(i2)) 。所以总体的方程就是: f[i]=A(2n1,i1)f[i1]f[i2](2n1(i2))(i1)
最后在乘上一个 m! 关于 mod 的逆元即可。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cctype>
typedef long long ll;
using namespace std;
const int mod=100000007;
const int M=1000010;
ll f[M],A[M],n,m,ans;
void in(ll &x)
{
    char t=getchar();int f=1;x=0;
    while(!isdigit(t)){if(t=='-')f=-1;t=getchar();}
    while(isdigit(t)){x=x*10+t-48;t=getchar();}
    x*=f;
}
ll power(ll a,ll b)
{
    ll ans=1;
    for (;b;b>>=1,a=(a*a)%mod)
    if (b&1) ans=(ans*a)%mod;
    return ans;
}
void work()
{
    A[0]=1;
    for (int i=1;i<=m;++i) A[i]=(A[i-1]*((ans-i+1+mod)%mod))%mod;
}
int main()
{
    in(n),in(m);
    ans=power(2,n);--ans;
    if (ans<0) ans+=mod;
    work();
    for (int i=3;i<=m;++i)
    f[i]=((A[i-1]-f[i-1]-(f[i-2]*(i-1)%mod*(ans-(i-2)))%mod)+mod)%mod;
    ans=1;
    for (int i=2;i<=m;++i) ans=(ans*i+mod)%mod;
    f[m]=(f[m]*power(ans,mod-2)%mod+mod)%mod;
    cout<<(f[m]+mod)%mod;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值