二次元音游人

这是一股照亮混沌的令和时代互联网的一道光,给在电子的海洋里冲浪的阿宅们带来笑容

[HNOI2011] 卡农 题解

题目传送门

提交记录😭😭😭

[HNOI2011] 卡农

题目描述

众所周知卡农是一种复调音乐的写作技法,小余在听卡农音乐时灵感大发,发明了一种新的音乐谱写规则。

他将声音分成 \(n\) 个音阶,并将音乐分成若干个片段。音乐的每个片段都是由 \(1\)\(n\) 个音阶构成的和声,即从 \(n\) 个音阶中挑选若干个音阶同时演奏出来。

为了强调与卡农的不同,他规定任意两个片段所包含的音阶集合都不同。同时为了保持音乐的规律性,他还规定在一段音乐中每个音阶被奏响的次数为偶数。

现在的问题是:小余想知道包含 \(m\) 个片段的音乐一共有多少种。
两段音乐 \(a\)\(b\) 同种当且仅当将 \(a\) 的片段重新排列后可以得到 \(b\)。例如:假设 \(a\)\(\{\{1,2\},\{2,3\}\}\)\(b\)\(\{\{2,3\},\{1,2\}\}\),那么 \(a\)\(b\) 就是同种音乐。

答案对 \(10^8+7\) 取模。

输入格式

仅一行两个正整数 \(n,m\)

输出格式

输出一行一个整数表示答案。

样例 #1

样例输入 #1

2 3

样例输出 #1

1

提示

【数据范围】
对于 \(20\%\) 的数据,\(1\le n,m \le 5\)
对于 \(50\%\) 的数据,\(1\le n,m \le 3000\)
对于 \(100\%\) 的数据,\(1\le n,m \le 10^6\)

【样例解释】
音乐为 \(\{\{1\},\{2\},\{1,2\}\}\)

呃呃

打了半天一直RE20分 发布帖子求助后得到思路www🥵🥵🥵

思路、

一个集合的非空集合共有\(2^n-1\)

简化题意就是 \({1,2,3,4,...,n}\)元素构成的集合里面取\(m\)个子集使之满足

(1)所有选出的\(m\)个子集都不能为空

(2)所有选出的\(m\)个子集中,能存在两个完全一样的集合。

(3)所有选出的\(m\)个子集中,\(1\)\(n\)每个元素出现的次数必须是偶数

先考虑没有三个限制条件的情况

\(2^n-1\)个子集里取\(m\)个子集共有\(C_{2^n-1}^{m}\)种可能

然后需要减去不符合三个条件的情况:

使用二进制思想考虑

如果每个元素出现的次数都是偶数,那么它们的异或一定是0

例: \(114514 xor 114514xor1919810xor1919810=0\)

先考虑第三个条件

(3).1到\(n\)每个元素出现的次数必须是偶数

考虑第\(i\)个元素 \(dp[i]\)表示取第\(i\)个集合出现的方案数

我们发现,如果前\(m-1\)个集合确定了,那么第\(m\)个集合也是确定的

因为对于任意一个元素\(op\),在前\(m-1\)个集合内如果出现次数是奇数,为了满足条件\(3\),它在第\(m\)个集合内一定出现,满足偶数。

如果前\(m-1\)个集合内如果出现次数是偶数,为了满足条件\(3\),它在第\(m\)个集合内一定不出现,满足偶数

所以 加上限制\(3\)后,方案数为\(C_{2^n-1}^{i-1}\)

🥰

再考虑第一个条件

\((1).\)所有选出的\(m\)个子集都不能为空

对于\(dp[i]\),思考\(dp[i-1]\)的含义,根据上文定义,可知\(dp[i-1]\)意思是取第\(i-1\)个集合出现的方案数,此时它的元素一定有偶数个

最后一个集合为了满足条件\(3\),一定是空集,不合法

对于\(i\),我们可以发现出现空集的方案数就是\(dp[i-1]\),因为它就等于前\(i-1\)个集合中出现次数为偶数的方案数


或者前\(i-1\)个子集的异或为0,则有\(dp[i-1]\)种方案

🥰

考虑第二个限制条件

我们需要减去第\(i\)个子集和前面某个子集相同的情况

如果我们把相同的两个子集都删去,那么剩下的\(i-2\)个子集肯定合法,即方案数\(dp[i-2]\),对于第\(i\)个子集,因为删去了两个相同的两个子集,\(i\)的方案数为\(2^n-1-(i-2)\)\(i-2\)就是前面合法的位置

所以重复的方案数为:\(dp[i-2]*(2^n-1-(i-2))\)

考虑最后一个问题

有些 方案 被重复计算了怎么办?


最难思考的地方呜呜呜

我们随便找一个集合\(op\),假设这是最后一个确定的集合,这个集合有\(i\)种选择,所以每种方案重复枚举了i次

对于这个问题,我们只需考虑一个合法的 \(op\) 有多少种可能可以用这种方法得到。我们随便在 \(op\) 里面选一个集合,它可以成为最后被确定的集合来计算一次 \(op\),而这个集合显然有 \(|op|=i\) 种选择,于是我们把答案除以 i 即可。

\(dp[i]\)最后要\(×i\)の逆元

总结

\[dp[i]= \frac{(C_{2^n-1}^{i-1}-dp[i-1]-(dp[i-2]*(2^n-1-(i-2))))}{i} \]

考虑化简\(C_{2^n-1}^{i-1}\)

其他题都是预处理出阶乘和阶乘的逆元再用函数输出

 for(register int i=1;i<=1e6+5;i++){
        x[i]=x[i-1]*i%mod;
        y[i]=y[i-1]*(qpow(i,mod-2))%mod;
        // z[i]=qpow(i,mod-2)%mod;
    }
inline int C(int n,int m){
    if(n<m) return 0;
    return ((x[n]%mod*y[n-m]%mod)%mod*y[m]%mod)%mod;
}

但是我们可以发现 组合数不能这么算的。你代码中的 \(op\) 的值域是\(10^8+7\),远远超出了你预处理的范围 ,这里注意到,\(op\) 是固定的,而 \(i-1\) 比较小,所以可以直接递推

递推

\(C_{2^n-1}^{i-1}\Rightarrow\)

\(C_{op}^{2}=\frac{(op)×(op-1)}{2}\Rightarrow\)

\(C_{op}^{3}=\frac{C_{op}^{2}×(op-2)}{3}\Rightarrow\)

\(C_{op}^{4}=\frac{C_{op}^{3}×(op-3)}{4}\Rightarrow\)

\(......\)

\(C_{op}^{i}=\frac{C_{op}^{i-1}×(op-(i-1))}{i}\)

递推完了!

然后注意一下除法需要用费马小定理处理逆元,因为\(10^8+7\)太大,不能预处理,所以直接在使用的时候用快速幂处理

\(C\)的初值为

int c=((((op-1)*niyuan(2)))%mod*((op)%mod))%mod;

最后附上AC代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace Testify{
    inline int read(){
        int f(1),x(0);
        char ch=getchar();
        for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
        for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
        return f*x;
    }
    inline void Write(int x){
        if(x>9) Write(x/10);
        putchar(x%10+'0');
    }
    inline void write(int x){
        if(x<0) putchar('-'),x=-x;
        Write(x);
        putchar('\n');
    }
}
using namespace Testify;
const int mod=1e8+7;
const int N=1e6+114514;
int n,m,dp[N],x[N],y[N];
inline int qpow(int a,int b){
    int res=1;
    while(b){
        if(b&1) res=res*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return res;
}
inline int C(int n,int m){
    if(n<m) return 0;
    return ((x[n]%mod*y[n-m]%mod)%mod*y[m]%mod)%mod;
}
inline int niyuan(int n){
    return qpow(n,mod-2)%mod;
}
signed main(void){
    n=read(),m=read();
    x[0]=y[0]=1;
    int op=qpow(2,n)-1;
    op=((op%mod)+mod)%mod;
    // cerr<<"op: "<<op<<endl;
    if(op<m){
        write(0);
        return 0;
    }
    // for(register int i=1;i<=op+1;i++){
    //     x[i]=x[i-1]*i%mod;
    //     y[i]=y[i-1]*(qpow(i,mod-2))%mod;
    // }
    // int aa=x[op];
    // int c=(x[op]%mod*y[2]%mod*y[op-2]%mod)%mod;
  //  int c=op*(op+mod-1)%mod+qpow(2,mod-2)%mod;
    int c=((((op-1)*niyuan(2)))%mod*((op)%mod))%mod;
    // cerr<<"C: ";
    // clog<<C(op,2)<<endl;
    dp[1]=0,dp[2]=0;
    for(register int i=3;i<=m;i++){
        dp[i]=((c-dp[i-1]-(dp[i-2]*(op-(i-2))))%mod+mod)%mod*niyuan(i)%mod;
        // c=(c%mod*y[i]%mod*y[op-i]%mod)%mod;
        // c=c*(op-i+1+(int)mod)%mod*qpow(i,mod-2)%mod;
        // c=(c*(op-(i-1))*((qpow(i-1,mod-2)%mod)))%mod;
         c=((c*(((op-(i-1)))%mod+mod)%mod)%mod*niyuan(i))%mod;
        // c=c*(op-(i-1))
    }
    write(dp[m]);
    return 0;
}

posted @ 2023-06-14 11:13  超绝最可爱天使酱  阅读(123)  评论(2)    收藏  举报