您的位置:首页 > 其它

BZOJ2339: [HNOI2011]卡农

2015-12-04 21:54 351 查看
Description





Input

Output

Sample Input

Sample Output

HINT

Source

Day2

首先题目里说是无序的,但是不要管它,我们先把它看成有序的,最后除以一个m!m!即可。我们考虑补集转换,首先所有的子集个数应该是2n−12^n-1,我们定义f[i]f[i]为挑选ii个片段的合法的方案数,此时总数应该是A(2n−1,i−1)A(2^n-1,i-1)(排列数)。为什么是i−1i-1而不是ii呢?因为要保证总数是偶数,也就是说如果你确定了i−1i-1个片段第ii个片段也就确定了。而这样肯定多算了,具体来说有两部分:

1、如果前i−1i-1个已经合法,那么第ii个就是空集,这样肯定不合法,所以要减去f[i−1]f[i-1]。

2、如果根据前i−1i-1个确定出来的第ii个集合和前面的某一个重复,这样肯定是不合法的。

因为考虑顺序,所以那个和第ii个重复的集合有i−1i-1种位置,对于每种位置,当前的总数偶数去掉两个数之后还是偶数,所以剩下其他数的方案数为f[i−2]f[i-2]。然后我们需要算出有多少种可能重复的方案,因为我们已经确定了(i−2)(i-2)个位置,所以方案数为(2n−1−(i−2))(2^n-1-(i-2))。所以总体的方程就是:f[i]=A(2n−1,i−1)−f[i−1]−f[i−2]∗(2n−1−(i−2))∗(i−1)f[i]=A(2^n-1,i-1)-f[i-1]-f[i-2]*(2^n-1-(i-2))*(i-1)

最后在乘上一个m!m!关于modmod的逆元即可。

[code]#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: