HNOI2011 卡农

题目传送门

Description

\(n​\)种元素,构成\(m​\)个集合\((n,m\leq 10^6)\),保证集合互不相同且非空,且每个元素总出现次数为偶数,两种方案集合重新排列可互相得到算一种,求方案数。

Solution

开始做的时候不用管重新排列算重,只要最后除以\(m!\)即可。

\(f_i\)\(i​\)个子集的答案。

因为出现次数为偶数,所以确定前\(i-1\)个子集后,第\(i\)个也唯一确定。即选择\(i-1\)个子集的方案数,总共有\(2^n-1\)个子集,所以为\(A_{2^n-2}^{i-1}\)

然后有一些方案不符合要求,需要删去。

若第\(i\)个集合为空,那么前\(i-1\)个为一种合法方案,所以要减去\(f_{i-1}\)

若第\(i​\)个集合与第\(j(j<i)​\)个集合相同,那么剩下\(n-2​\)个集合为一种合法方案\(f_{n-2}​\)\(j​\)的位置有\(n-1​\)种取值,而当\(n-2​\)中确定后,\(i​\)\(j​\)的取法为\(2^n-1-(n-2)​\)。所以需减去\(f_{i-2}\cdot (i-1)\cdot (2^n-i+1)​\)

综上所述:

\[f_i=A_{2^n-1}^{i-1}-f_{i-1}-f_{i-2}\cdot (i-1)\cdot (2^n-i+1) \]

\[Ans=\frac{f_m}{m!} \]

Code

#include<bits/stdc++.h>
using namespace std;
const int N=1000005, P=100000007;
int A[N], F[N];

inline int read()
{
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}

int Pow(int x, int t)
{
    int res=1;
    while (t) t&1?res=1ll*res*x%P:0, x=1ll*x*x%P, t>>=1;
    return res;
}

int main()
{
    int n=read(), m=read(), t=Pow(2, n), p=1; A[0]=1; F[F[1]=0]=1;
    for (int i=1; i<=m; i++) A[i]=1ll*A[i-1]*(t-i)%P, p=1ll*p*i%P;
    for (int i=2; i<=m; i++) F[i]=(A[i-1]-F[i-1]-1ll*F[i-2]*(i-1)%P*(t-i+1)%P)%P;
    printf("%d\n", ((1ll*F[m]*Pow(p, P-2)%P)+P)%P);
    return 0;
}

posted @ 2019-04-20 22:10  OIerC  阅读(144)  评论(0编辑  收藏  举报