[BZOJ2111]:[ZJOI2010]Perm 排列计数(组合数学)
题目传送门
题目描述
称一个1,2,...,N的排列${P}_{1}$,${P}_{2}$,...,${P}_{N}$是Magic的,当且仅当2≤i≤N时,${P}_{i}$>${P}_{\frac{i}{2}}$。计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值。
输入格式
输入文件的第一行包含两个整数n和p,含义如上所述。
输出格式
输出文件中仅包含一个整数,表示计算1,2,⋯, N的排列中, Magic排列的个数模p的值。
样例
样例输入:
20 23
样例输出:
16
数据范围与提示
100%的数据中,1≤N ≤ ${10}^{6}$,P≤${10}^{9}$,p是一个质数。 数据有所加强
题解
来介绍两种做法:
1.递推+组合数学:
前缀知识:
1)组合数学入门。
2)Lucas定理。
网上有好多人管这个叫DP,个人感觉不是DP,可能是一开始有一个叫它DP,于是好多半瓶子醋的就也叫它DP了叭~
首先,看到这道题我就想到这是一个小跟堆。
你去想想,小跟堆是一个完全二叉树,所以每一个节点i的两个儿子分别是i>>1和i>>1|1,所以P[i/2]其实就是P[i]的父亲,然后又因为P[i/2]<P[i],所以这就是一个小跟堆啦~
那么问题就转化为,求这n个数所能组成的小跟堆的数量。
然后我们定义DP[i]表示i这个点为跟节点的方案数。
对于没一个点的方案数,显然只能从他的两个儿子转移过来,即为:DP[i]=DP[i>>1]×DP[i>>1|1]。
但是显然答案不能直接这样的来,因为每个点i的两个子树中的数可以不一样,所以会漏掉答案。
所以我们就要融入一些组合数学的问题了。
因为只是交换两棵子树之间的数,每棵子树的size是不变的,所以我们就相当于是要乘上在size[leftson]+size[rightson]当中取size[leftson]个数的方案数。
于是式子变为了:DP[i]=DP[i>>1]×DP[i>>1|1]×C(size[i]-1,size[i>>1])。
注意P是一个小的质数,所以考虑Lucas求组合数。
2.拓扑排序:
偶然间发现这道题就是在求一棵树的拓扑排序的数量(笔者正在证明)。
直接套用公式:ans=$\frac{n!}{\prod \limits_{i=1}^{n} size[i] }$。
接着就是推式子。
DP[u]=[(size[u]-1)!×∏DP[v] ]/∏size[v](其中,v为u的儿子)。
接着定义g[u]=$\frac{DP[u]}{size[u]!}$=$\frac{\prod g[v]}{size[u]}$=$\frac{1}{\prod size[k]}$(其中,k为u子树上的点)。
所以g[1]=$\frac{1}{\prod \limits_{i=1}^{n} size[i] }$。
所以答案即为:ans=DP[1]=g[1]×size[1]!=$\frac{n!}{\prod \limits_{i=1}^{n} size[i] }$。
但是注意数据加强之后可能会出现mod p=0的情况,注意附成1即可。
代码时刻
解法1:
#include<bits/stdc++.h>
using namespace std;
long long n,p;
long long jc[1000001],qsm[1000001],dp[1000001],size[1000001];
long long qpow(long long x,long long y)
{
long long ans=1;
while(y)
{
if(y%2)ans=(ans*x)%p;
y>>=1;
x=(x*x)%p;
}
return ans;
}
void pre_work()
{
jc[0]=1;
for(long long i=1;i<=n;i++)
jc[i]=(jc[i-1]*i)%p;
for(long long i=0;i<=n;i++)
qsm[i]=qpow(jc[i],p-2)%p;
}
long long get_C(long long x,long long y){return ((jc[x]*qsm[y])%p*qsm[x-y])%p;}
long long lucas(long long x,long long y)
{
if(!y)return 1;
return (get_C(x%p,y%p)*lucas(x/p,y/p))%p;
}
int main()
{
scanf("%lld%lld",&n,&p);
pre_work();
for(long long i=n;i>0;i--)
{
if((i<<1)>n&&(i<<1|1)>n)
{
dp[i]=1;
size[i]=1;
continue;
}
if((i<<1|1)>n)
{
dp[i]=1;
size[i]=2;
continue;
}
size[i]=size[i<<1]+size[i<<1|1]+1;
dp[i]=lucas(size[i]-1,size[i<<1]);
if((i<<1)<=n)dp[i]=(dp[i]*dp[i<<1])%p;
if((i<<1|1)<=n)dp[i]=(dp[i]*dp[i<<1|1])%p;
}
cout<<dp[1]<<endl;
return 0;
}
解法2:
#include<bits/stdc++.h>
using namespace std;
long long n,p,s[1000001],ans=1,i,inv[1000001];
int main()
{
scanf("%lld%lld",&n,&p);
inv[1]=inv[0]=1;
for(i=1;i<=n;i++)s[i]=1;
for(i=n;i>=2;i--)s[i>>1]+=s[i];
for(i=2;i<=n;i++)inv[i]=((1LL*(-p/i)*inv[p%i])%p+p)%p;
for(i=1;i<=n;i++)
{
ans=ans*i%p;
ans=max(ans,1LL);//%p=0附成1
ans=ans*inv[s[i]]%p;
ans=max(ans,1LL);//%p=0附成1
}
printf("%lld",ans);
return 0;
}
rp++
[BZOJ2111]:[ZJOI2010]Perm 排列计数(组合数学)的更多相关文章
- [BZOJ2111][ZJOI2010]Perm排列计数(组合数学)
题意就是求一个n个点的堆的合法形态数. 显然,给定堆中所有数的集合,则这个堆的根是确定的,而由于堆是完全二叉树,所以每个点左右子树的大小也是确定的. 设以i为根的堆的形态数为F(i),所以F(i)+= ...
- BZOJ2111: [ZJOI2010]Perm 排列计数
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2111 题意:一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2< ...
- [bzoj2111][ZJOI2010]Perm 排列计数 ——问题转换,建立数学模型
题目大意 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很 ...
- 【BZOJ2111】[ZJOI2010]Perm 排列计数 组合数
[BZOJ2111][ZJOI2010]Perm 排列计数 Description 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi> ...
- BZOJ 2111: [ZJOI2010]Perm 排列计数 [Lucas定理]
2111: [ZJOI2010]Perm 排列计数 Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 1936 Solved: 477[Submit][ ...
- 2111: [ZJOI2010]Perm 排列计数
2111: [ZJOI2010]Perm 排列计数 链接 题意: 称一个1,2,...,N的排列$P_1,P_2...,P_n$是Magic的,当且仅当$2<=i<=N$时,$P_i> ...
- bzoj 2111: [ZJOI2010]Perm 排列计数 (dp+卢卡斯定理)
bzoj 2111: [ZJOI2010]Perm 排列计数 1 ≤ N ≤ 10^6, P≤ 10^9 题意:求1~N的排列有多少种小根堆 1: #include<cstdio> 2: ...
- BZOJ_2111_[ZJOI2010]Perm 排列计数_树形DP+组合数学
Description 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic ...
- 【bzoj2111】[ZJOI2010]Perm 排列计数 dp+Lucas定理
题目描述 称一个1,2,...,N的排列P1,P2...,Pn是Mogic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Mogic的,答案可能很 ...
随机推荐
- http://www.pythontutor.com/visualize.html#mode=edit python在线检测代码
http://www.pythontutor.com/visualize.html#mode=edit
- php开发环境推荐使用
万丈高楼平地起,好用得环境才能建立宏伟大厦,php开发环境推荐使用 1,自己安装 lamp 环境 linux+apache+mysql+php 2,自己安装 lnmp 环境 linux+nginx+m ...
- linux:用户和组文件解释(/etc/passwd、/etc/shadow、/etc/group、/etc/gshadow)
一.用户文件:/etc/passwd [root@pinfun6 ~]# cat /etc/passwd root:x:0:0:root:/root:/bin/bash 1 2 3 4 5 6 7 | ...
- 看电视剧<潜伏>有感
前几天看了老电视剧-潜伏,有一些感慨. 一,立场和真相都不重要,形式才是最重要的. 二.历史在不断的轮回中. 好汉历经千辛万苦杀掉了为害一方的恶霸,好汉的威望达到了顶峰,自然的成了村庄的守护者和掌控者 ...
- npm学习(八)之如何使用语义化版本
npm的语义化版本控制——Semantic versioning 在新发布的代码中传达更改的程度非常重要,因为有时更新会破坏包需要的代码(称为依赖项).语义化版本控制(semver)是一个旨在解决这个 ...
- 字符串连连看 (和hihocoder 字符消除类似)
题目描述 对于输入的字符串,从左到右扫描字符串,如果存在由三个以上(包括三个)连续相同字符组成的子串,就将这个子串从原串中去掉,并将原有字符串剩下的部分拼接到一起.重复上述过程,直到无法去掉任何子串 ...
- CNN与图像高级应用
一.图像识别与定位 思路1:视作回归 4个数字,用L2 loss/欧氏距离损失(x,y,w,h)这四个数都是连续值 思路2:借助图像窗口 二.物体识别 0.图像识别与定位: (1)Classifica ...
- 3-关于ES的几个小疑问和解答
1.ES如何实现分布式 2.ES如何实现高实时 3.ES如何实现高扩展 4.ES7.x版本为何废弃type 5.搜索原理--知乎es
- iOS 支付 [支付宝、银联、微信]
这是开头语 前不久做了一个项目,涉及到支付宝和银联支付,支付宝和银联都是业界的老大哥,文档.SDK都是很屌,屌的找不到,屌的看不懂,屌到没朋友(吐槽而已),本文将涉及到的最新可用SDK.文档,以及本人 ...
- 人工智能AI从入门到精通所有视频教程(140G)以及数据资料免费拿
包含了人工智能AI从入门到精通所有视频教程(140G). 资料获取方式,关注公总号RaoRao1994,查看往期精彩-所有文章,即可获取资源下载链接 更多资源获取,请关注公总号RaoRao1994