zoukankan      html  css  js  c++  java
  • luogu2606 排列计数

    题目大意

      求满足下列条件的排列$P$的数量:$forall P_i, P_i>P_{lfloor frac{i}{2} floor}$。

    思路

    从下标入手

      反过来想,也就是对$forall P_i, P_i<P_{2i}且P_i<P_{2i+1}$。因为小根堆中一个“小三角”中节点的编号满足:若顶部编号为$i$,则左下角节点编号为"2i",右下角为$2i+1$,因此题目就是要让我们求大小为$n$的小根堆的数量。

    递归式

      因为堆这个结构有“子堆”这个子结构,所以可以递归。定义$l(n)$为大小为$n$的堆的左子堆大小,$f(i)$为大小为$i$,所有节点的值的取值范围一定(但并没有具体指定)时都不相等的堆有多少个。该堆的左子堆的个数等于当左子堆所有节点的值的取值范围的种数($C_{n-1}^{l(n)}$)乘以当所有节点的值的取值范围一定时的堆数($f(l(n))$)。分析完左子堆,随后还要乘以右子堆的堆数($f(r(n))$)。由于左子堆取值范围的种数确定了,右子堆的也确定了,所以不用再次乘以$C_{n-1}^{r(n)}$了。故总递归式为:

    $$f(n)=C_{n-1}^{l(n)}f(l(n))f(r(n))$$

      注意求组合数时要用Lucas定理取模。

    怎么求$l(n),r(n)$?

      注意这个没有通项公式,要按照堆的顺序递归解决。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    #define ll long long
    const int MAX_N = 1000010;
    ll F[MAX_N], Fact[MAX_N];
    int Size_Lsize[MAX_N], Size_Rsize[MAX_N];
    
    int GetSize(int curNode, int n)
    {
    	if (curNode > n)
    		return 0;
    	int lSize = GetSize(curNode * 2, n), rSize = GetSize(curNode * 2 + 1, n), curSize = lSize + rSize + 1;
    	Size_Lsize[curSize] = lSize, Size_Rsize[curSize] = rSize;
    	return curSize;
    }
    
    void GetFact(int n, int p)
    {
    	Fact[0] = Fact[1] = 1;
    	for (ll i = 2; i <= n; i++)
    		Fact[i] = i * Fact[i - 1] % p;
    }
    
    ll Mult(ll a, ll b, ll p)
    {
    	ll ans = 0;
    	while (b)
    	{
    		if (b & 1)
    			ans = (ans + a) % p;
    		a = (a + a) % p;
    		b >>= 1;
    	}
    	return ans;
    }
    
    ll Power(ll a, ll n, ll p)
    {
    	ll ans = 1;
    	while (n)
    	{
    		if (n & 1)
    			ans = Mult(ans, a, p);
    		a = Mult(a, a, p);
    		n >>= 1;
    	}
    	return ans;
    }
    
    ll Inv(ll a, ll p)
    {
    	return Power(a, p - 2, p);
    }
    
    ll Comb(int n, int m, int p)
    {
    	return Fact[n] * Inv(Mult(Fact[n - m], Fact[m], p), p);
    }
    
    ll Lucas(int n, int m, int p)
    {
    	if (m == 0)
    		return 1;
    	return Comb(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
    }
    
    ll Dfs(int n, int p)
    {
    	if (F[n])
    		return F[n];
    	if (n == 1 || n == 0)
    		return F[n] = 1;
    	return F[n] = Dfs(Size_Lsize[n], p) * Dfs(Size_Rsize[n], p) % p * Lucas(n - 1, Size_Lsize[n], p) % p;//易忘点:Dfs后的%p
    }
    
    int main()
    {
    	int n;
    	ll p;
    	scanf("%d%lld", &n, &p);
    	GetSize(1, n);
    	GetFact(n, p);
    	printf("%lld
    ", Dfs(n, p));
    	return 0;
    }
    

      

  • 相关阅读:
    c++语言 运算符重载 使用重载运算符实现类的加法运算
    c++语言 类模板的使用 类模板的实现
    C++语言 通过类模板实现加法计算器
    C++语言 对动物的行为实现多态
    c++语言 友元类和友元方法 将普通函数声明为友元函数
    C++语言 通过构造函数初始化学生信息
    c++语言 静态成员数据和静态方法
    欧拉回路心得
    POJ2965 The Pilots Brothers' refrigerator(枚举)
    HDU1269 迷宫城堡(有向图的强连通分量(scc))
  • 原文地址:https://www.cnblogs.com/headboy2002/p/9163185.html
Copyright © 2011-2022 走看看