[HNOI2011]卡农 (数论计数,DP)
题面
众所周知卡农是一种复调音乐的写作技法,小余在听卡农音乐时灵感大发,发明了一种新的音乐谱写规则。
他将声音分成
n
n
n 个音阶,并将音乐分成若干个片段。音乐的每个片段都是由
1
1
1 到
n
n
n 个音阶构成的和声,即从
n
n
n 个音阶中挑选若干个音阶同时演奏出来。
为了强调与卡农的不同,他规定任意两个片段所包含的音阶集合都不同。同时为了保持音乐的规律性,他还规定在一段音乐中每个音阶被奏响的次数为偶数。(注:“一段音乐”指整个曲子)
现在的问题是:小余想知道包含
m
m
m 个片段的音乐一共有多少种。
两段音乐
a
a
a 和
b
b
b 同种当且仅当将
a
a
a 的片段重新排列后可以得到
b
b
b。例如:假设
a
a
a 为
{
{
1
,
2
}
,
{
2
,
3
}
}
\{\{1,2\},\{2,3\}\}
{{1,2},{2,3}},
b
b
b 为
{
{
2
,
3
}
,
{
1
,
2
}
}
\{\{2,3\},\{1,2\}\}
{{2,3},{1,2}},那么
a
a
a 与
b
b
b 就是同种音乐。
答案对
1
0
8
+
7
10^8+7
108+7 取模。
输入格式
仅一行两个正整数
n
,
m
n,m
n,m 。
输出格式
输出一行一个整数表示答案。
输入输出样例
输入 #1
2 3
输出 #1
1
说明/提示
【数据范围】
对于
20
%
20\%
20% 的数据,
1
≤
n
,
m
≤
5
1\le n,m \le 5
1≤n,m≤5;
对于
50
%
50\%
50% 的数据,
1
≤
n
,
m
≤
3000
1\le n,m \le 3000
1≤n,m≤3000;
对于
100
%
100\%
100% 的数据,
1
≤
n
,
m
≤
1
0
6
1\le n,m \le 10^6
1≤n,m≤106 。
【样例解释】
音乐为
{
{
1
}
,
{
2
}
,
{
1
,
2
}
}
\{\{1\},\{2\},\{1,2\}\}
{{1},{2},{1,2}} 。
题解
往容斥上面想,但是别想多了。
看了别人的题解发现其实并不难。
首先应该清楚,由于每个片段不相同,所以题目中关于 “同种音乐” 的限制可以去除掉,最后再乘上
(
m
!
)
−
1
(m!)^{-1}
(m!)−1 就是了。
然后,结合 DP 来容斥,我们相当于要达到三个条件:
- 每一种音阶出现次数是偶数。
- 无空集。
- 不存在两片段相同。
我们令
d
p
[
i
]
\mathrm{dp}[i]
dp[i] 表示前
i
i
i 个片段满足上述条件的方案数。
按照容斥的思路,我们应该先把全集求出来,再依次减去不合法。但是很容易发现,要把不符合第一个条件的去掉极其困难。
我们其实可以先求出只满足第一条的方案总数。有一个发现:如果随意确定了前
i
−
1
i-1
i−1 个片段,那么为了满足第一个条件,可以直接据此推出第
i
i
i 个片段。那么我们就可以定这个方案数为:
A
2
n
−
1
i
−
1
A_{2^n-1}^{i-1}
A2n−1i−1 ,意为确定前
i
−
1
i-1
i−1 个片段。这样做还有一个好处,那就是单独看前
i
−
1
i-1
i−1 个片段时,都是满足第二条和第三条限制的。
这就给我们的容斥带来了极大的便利。去除空集情况,达到第二条限制,很简单,如果有空集就一定是第
i
i
i 个片段空集,那么前
i
−
1
i-1
i−1 个片段就满足了三个条件,因此有空集的不合法情况数为
d
p
[
i
−
1
]
\mathrm{dp}[i-1]
dp[i−1] 。
接下来要去除存在片段相同的情况。由于前
i
−
1
i-1
i−1 个片段没有彼此相同的,因此只能是第
i
i
i 个片段与前面的片段相同,我们就令其为第
j
j
j 个片段。我们在
1
1
1 到
i
−
1
i-1
i−1 中枚举
j
j
j ,一共
i
−
1
i-1
i−1 种情况,再确定
i
i
i 和
j
j
j 具体应该为哪一种组合,由于
j
j
j 在前面
i
−
1
i-1
i−1 个片段中是独一无二的,所以一共有
2
n
−
1
−
(
i
−
2
)
=
2
n
−
i
+
1
2^n-1-(i-2)=2^n-i+1
2n−1−(i−2)=2n−i+1 种选择。
然后把
i
i
i 和
j
j
j 同时拿走,剩下的
i
−
2
i-2
i−2 个位置同时满足三个条件,故片段相同的不合法情况数为
d
p
[
i
−
2
]
∗
(
i
−
1
)
∗
(
2
n
−
i
+
1
)
{\rm dp}[i-2]*(i-1)*(2^n-i+1)
dp[i−2]∗(i−1)∗(2n−i+1) 。
综上,总的转移方程为:
d
p
[
i
]
=
A
2
n
−
1
i
−
1
−
d
p
[
i
−
1
]
−
d
p
[
i
−
2
]
∗
(
i
−
1
)
∗
(
2
n
−
i
+
1
)
{\rm dp}[i]=A_{2^n-1}^{i-1}-{\rm dp}[i-1]-{\rm dp}[i-2]*(i-1)*(2^n-i+1)
dp[i]=A2n−1i−1−dp[i−1]−dp[i−2]∗(i−1)∗(2n−i+1) 。时间复杂度
O
(
n
)
O(n)
O(n) 。
关于排列数
A
2
n
−
1
i
−
1
A_{2^n-1}^{i-1}
A2n−1i−1 的计算,它其实就是最多
m
−
1
m-1
m−1 个数相乘。
CODE
#include<map>
#include<cmath>
#include<queue>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 1000005
#define ENDL putchar('\n')
#define LL long long
#define DB double
#define lowbit(x) ((-x) & (x))
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
void putuint(int x) {
if(!x) return ;
putuint(x/10);putchar(x%10+'0');
}
void putint(int x) {if(x==0)putchar('0');if(x<0)putchar('-'),x=-x;putuint(x);}
const int MOD = 100000007;
int n,m,i,j,s,o,k;
int qkpow(int a,int b) {
int res = 1;
while(b > 0) {
if(b & 1) res = res *1ll* a % MOD;
a = a *1ll* a % MOD; b >>= 1;
}return res;
}
int fac[MAXN]={1,1},inv[MAXN]={1,1},invf[MAXN]={1,1};
int dp[MAXN];
int main() {
n = read();m = read();
for(int i = 2;i <= n||i <= m;i ++) {
fac[i] = fac[i-1] *1ll* i % MOD;
inv[i] = (MOD-inv[MOD%i]) *1ll* (MOD/i) % MOD;
invf[i] = invf[i-1] *1ll* inv[i] % MOD;
}
dp[0] = 1;dp[1] = 0;
int po = qkpow(2,n)-1,A = 1,ful = po;
for(int i = 2;i <= m;i ++) {
A = A *1ll* po % MOD;
(po += MOD-1) %= MOD;
dp[i] = (0ll+ A +MOD- dp[i-1] +MOD- dp[i-2] *1ll* (i-1) % MOD *1ll* (ful +MOD- (i-2)) % MOD) % MOD;
}
int as = dp[m] *1ll* invf[m] % MOD;
printf("%d\n",as);
return 0;
}
[HNOI2011]卡农 (数论计数,DP)的更多相关文章
- Luogu3214 HNOI2011 卡农 组合、DP
传送门 火题qwq 我们需要求的是满足元素个数为\(M\).元素取值范围为\([1,2^n-1]\).元素异或和为\(0\)的集合的数量. 首先我们可以计算元素有序的方案数(即计算满足这些条件的序列的 ...
- 洛谷 P3214 - [HNOI2011]卡农(线性 dp)
洛谷题面传送门 又是一道我不会的代码超短的题( 一开始想着用生成函数搞,结果怎么都搞不粗来/ll 首先不妨假设音阶之间存在顺序关系,最终答案除以 \(m!\) 即可. 本题个人认为一个比较亮的地方在于 ...
- bzoj2339[HNOI2011]卡农 dp+容斥
2339: [HNOI2011]卡农 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 842 Solved: 510[Submit][Status][ ...
- P3214 [HNOI2011]卡农
题目 P3214 [HNOI2011]卡农 在被一题容斥\(dp\)完虐之后,打算做一做集合容斥这类的题了 第一次深感HNOI的毒瘤(题做得太少了!!) 做法 求\([1,n]\)组成的集合中选\(m ...
- 【BZOJ2339】[HNOI2011]卡农 组合数+容斥
[BZOJ2339][HNOI2011]卡农 题解:虽然集合具有无序性,但是为了方便,我们先考虑有序的情况,最后将答案除以m!即可. 考虑DP.如果我们已经知道了前m-1个集合,那么第m个集合已经是确 ...
- [BZOJ2339][HNOI2011]卡农
[BZOJ2339][HNOI2011]卡农 试题描述 输入 见"试题描述" 输出 见"试题描述" 输入示例 见"试题描述" 输出示例 见& ...
- BZOJ2339[HNOI2011]卡农——递推+组合数
题目链接: [HNOI2011]卡农 题目要求从$S=\{1,2,3……n\}$中选出$m$个子集满足以下三个条件: 1.不能选空集 2.不能选相同的两个子集 3.每种元素出现次数必须为偶数次 我们考 ...
- [HNOI2011]卡农
题目描述 众所周知卡农是一种复调音乐的写作技法,小余在听卡农音乐时灵感大发,发明了一种新的音乐谱写规则.他将声音分成 n 个音阶,并将音乐分成若干个片段.音乐的每个片段都是由 1 到 n 个音阶构成的 ...
- [HNOI2011]卡农 题解
题目描述 众所周知卡农是一种复调音乐的写作技法,小余在听卡农音乐时灵感大发,发明了一种新的音乐谱写规则.他将声音分成 n 个音阶,并将音乐分成若干个片段.音乐的每个片段都是由 1 到 n 个音阶构成的 ...
随机推荐
- React项目配置npm run build命令分环境打包
使用create-react-app脚手架创建的项目默认隐藏了webpack等配置文件信息,使用npm run eject命令暴露这些隐藏的配置文件信息 项目默认有两个环境:开发环境(npm star ...
- TypeScript(5)类、继承、多态
前言 对于传统的 JavaScript 程序我们会使用函数和基于原型的继承来创建可重用的组件,但对于熟悉使用面向对象方式的程序员使用这些语法就有些棘手,因为他们用的是基于类的继承并且对象是由类构建出来 ...
- SAP Web Dynpro - 教程
SAP Web Dynpro是一种标准的SAP UI技术,用于使用图形工具和与ABAP工作台集成的开发环境来开发Web应用程序. 图形工具的使用减少了实施工作,并有助于维护ABAP工作台中的组件. 本 ...
- 机械硬盘和ssd固态硬盘的原理对比分析
固态硬盘和机械硬盘的区别 机械硬盘 磁头是不是直接和盘片接触的呢 磁盘中有几个盘片 机械硬盘的工作原理 固态硬盘的寻址方式 SMR叠瓦式真的比PMR优秀吗 固态硬盘 主控芯片 闪存颗粒 缓存单元 固态 ...
- StringJoiner的使用
1.添加字符串 add()方法 StringJoiner sj = new StringJoiner(","); sj.add("我爱你"); sj.add(& ...
- 互联网研发效能之去哪儿网(Qunar)核心领域DevOps落地实践
本文从业务目标角度出发,确定了开源+自建模式搭建 Qunar 研发工具链整体生态:通过 APPCODE 打通工具链,流程规范化自动化:多种手段+发布门禁助力质量提升:建立应用画像确定运维最小单元,可发 ...
- jenkins页面一直在Please wait while Jenkins is getting ready to work ...
原因:因为访问官网太慢.我们只需要换一个源,不使用官网的源即可. 1.找到jenkins工作目录 find / -name *.UpdateCenter.xml 2.修改文件中的url,随后重启就行了 ...
- 在linux上配置Maven环境变量
1.首先下载maven ,这里我使用的是3.8.1 Maven – Download Apache Maven 2.在linux环境中,将maven上传至 /usr/local/目录中 这里我将mav ...
- CF1656E Equal Tree Sums 题解
题目链接 思路分析 自认为是一道很好的构造题,但是我并不会做. 看了题解后有一些理解,在这里再梳理一遍巧妙的思路. 我们先来看这样的一张图: 我们发现当去掉叶子节点的父亲时,剩下树的价值和等于叶子节点 ...
- Pytorch Tensor 维度的扩充和压缩
维度扩展 x.unsqueeze(n) 在 n 号位置添加一个维度 例子: import torch x = torch.rand(3,2) x1 = x.unsqueeze(0) # 在第一维的位置 ...