zoukankan      html  css  js  c++  java
  • 【洛谷P3214】卡农

    题目

    题目链接:https://www.luogu.com.cn/problem/P3214
    众所周知卡农是一种复调音乐的写作技法,小余在听卡农音乐时灵感大发,发明了一种新的音乐谱写规则。
    他将声音分成 (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,mleq 10^6)

    思路

    理一下题目内容发现我们需要求出满足以下几个条件的序列数量:

    • 序列中每一个元素所表示的集合元素均在 ([1,n]) 中且集合互不相同。
    • 每一个数字在所有元素中出现次数之和为偶数。

    不难发现我们不需要考虑两个序列“本质相同”,因为最后直接除以 (m!) 即可。
    考虑 dp。设 (f_i) 表示序列前 (i) 个元素有多少种方案是合法的。那么考虑容斥:

    1. 无视所有条件,当我们确定了前 (i-1) 个元素(保证两两不同)时,我们一定可以唯一确定第 (i) 个元素,因为我们转化成了有标号的,所以方案数就是 (inom{2^n-1}{i-1}i!)
    2. 考虑减去重复的情况,假设第 (i) 个集合与第 (j(j<i)) 个集合完全一致,那么同时删去这两个元素能得到一个长度为 (i-2) 的合法序列。除去这 (i-2) 个元素以外,(i,j)(2^n-1-(i-2)) 种可能,(j) 可以被放在序列 (i-1) 个空隙之间,所以重复的数量是 ((i-1)(2^n-1-(i+1))f_{i-2})
    3. 还要减去前 (i-1) 个元素已经保证了每一个数字各出现偶数次,这样第 (i) 个元素唯一确定是空集,不合法。方案数显然是 (f_{i-1})

    所以我们得到

    [f_i=inom{2^n-1}{i-1}i!-(i-1)(2^n-1-(i+1))f_{i-2}-f_{i-1} ]

    组合数递推,其他预处理即可。时间复杂度 (O(n+m))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=1000010,MOD=1e8+7;
    int n,m;
    ll power,f[N],g[N],fac[N],inv[N];
    
    ll fpow(ll x,ll k)
    {
    	ll ans=1;
    	for (;k;k>>=1,x=x*x%MOD)
    		if (k&1) ans=ans*x%MOD;
    	return ans;
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	fac[0]=inv[0]=inv[1]=g[0]=f[0]=1;
    	power=fpow(2,n);
    	for (int i=1;i<=m;i++)
    	{
    		fac[i]=fac[i-1]*i%MOD;
    		if (i>1) inv[i]=-1LL*MOD/i*inv[MOD%i]%MOD;
    		g[i]=g[i-1]*(power-i)%MOD*inv[i]%MOD;
    		f[i]=(g[i-1]*fac[i-1]-f[i-1])%MOD;
    		if (i>1) f[i]=(f[i]-f[i-2]*(power-1-(i-2))%MOD*(i-1))%MOD;
    	}
    	printf("%lld",(f[m]*fpow(fac[m],MOD-2)%MOD+MOD)%MOD);
    	return 0;
    }
    
  • 相关阅读:
    List Curry
    List Polymorphic
    List Fibonacci
    搭建 docker + nginx + keepalived 实现Web应用的高可用(亲测)
    Java 大数相乘、大数相加、大数相减
    剑指offer —— 从尾到头打印链表
    剑指offer —— 替换空格
    剑指offer —— 二维数组的查找
    JDK源码 Integer.bitCount(i)
    Springboot 实现前台动态配置数据源 (修改数据源之后自动重启)
  • 原文地址:https://www.cnblogs.com/stoorz/p/14330285.html
Copyright © 2011-2022 走看看