首页 > 其他分享 >洛谷 P2606 [ZJOI2010]排列计数 题解

洛谷 P2606 [ZJOI2010]排列计数 题解

时间:2022-11-06 20:47:34浏览次数:79  
标签:dots 子树 洛谷 题解 ZJOI2010 P2606 define

Luogu P2606 [ZJOI2010]排列计数 题解

题目描述

称一个 \(1 \sim n\) 的排列 \(p_1,p_2, \dots ,p_n\) 是 Magic 的,当且仅当

\[\forall i \in [2,n],p_i > p_{\lfloor i/2 \rfloor}. \]

计算 \(1 \sim n\) 的排列中有多少是 Magic 的,答案可能很大,只需输出模 \(m\) 以后的值。

思路

把题目中给的条件转化一下,变成:对于每一个 \(i\),满足 \(p_i<p_{2i}\) 且 \(p_i<p_{2i+1}\)。

发现 \(2i\) 和 \(2i+1\) 即为二叉树上节点 \(i\) 的两个儿子,上述性质符合小根堆的性质。

设 \(s_i\) 表示以 \(i\) 为根的子树大小,\(f_i\) 表示用 \(1,2,\dots,s_i\) 填满以 \(i\) 为根的子树且满足上述性质的方案数。显然,用 \(1,2,\dots,s_i\) 和 \(2,3,\dots,s_i+1\) 填的方案是等效的,即方案数与 \(i\) 本身的值无关,而与 \(s_i\) 的大小有关。

考虑在二叉树上 DP:从叶子到根,先由子树的大小算出 \(s_i\),然后再由子树的方案数和 \(s_i\)算出 \(f_i\)。设两个子树分别是 \(l\) 和 \(r\),则需要从 \(s_i\) 个数中选出 \(s_{l}\) 和 \(s_{r}\) 个分别给两个子树,方案数为 \(\dbinom{s_i-1}{s_{l}}\)。于是,根据分步乘法原理,转移方程为

\[f_i=f_{l}\times f_{r}\times\dbinom{s_i-1}{s_{l}}. \]

最后的答案为 \(f_1\)。

代码

  • 由于叶子节点没有子树,\(f\) 数组的初值须全部设为 \(1\),不然会输出 0
  • 并且,\(s\) 数组和 \(f\) 数组需要开大一倍,不然会 RE;
  • 由于模数很小,可以用 Lucas 定理求组合数。
  • (预处理出阶乘比直接算要快 0.4 秒左右,不过都能过)
  • fill 函数是 stl 库中用于给数组内所有元素赋值的函数,与 memset 不同)
  • (十年 OI 一场空,不开 long long 见祖宗)

(快速幂模板省略)

#include <iostream>
#include <algorithm>
#define f(x, y, z) for (int x = (y); (x) <= (z); ++(x))
#define g(x, y, z) for (int x = (y); (x) >= (z); --(x))
#define il inline
#define ls (i << 1)
#define rs (i << 1 | 1)
#define int ll
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int n, p, s[N << 1], f[N << 1];
int fac[N];

il void Init(int p) {
	fac[0] = fac[1] = 1;
	f(i, 2, n) fac[i] = fac[i - 1] * i % p;
}

il int ksm(int a, int x, int p) { ... }

il int C(int n, int m, int p) {
//	int a = 1, b = 1;
//	f(i, n - m + 1, n) a = a * i % p;
//	f(i, 2, m) b = b * i % p;
//	return a * ksm(b, p - 2, p) % p;
	return fac[n] * ksm(fac[m] * fac[n - m] % p, p - 2, p) % p;
}

int Lucas(int n, int m, int p) {
	if (!m) return 1;
	return C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
}

signed main() {
	
	cin >> n >> p;
	Init(p);
	fill(f, f + (N << 1), 1);
	g(i, n, 1) {
		s[i] = s[ls] + s[rs] + 1;
		f[i] = f[ls] * f[rs] % p * Lucas(s[i] - 1, s[ls], p) % p;
	}
	cout << f[1] << '\n';
	
	return 0;
}

标签:dots,子树,洛谷,题解,ZJOI2010,P2606,define
From: https://www.cnblogs.com/f2021ljh/p/16863839.html

相关文章

  • CSP-J1、S1 2021 赛后总结+简要题解
    postedon2021-09-1922:34:52|under题解|source人在佛山,考场在南外。学校信息队太强了,不仅租车还包午饭,点赞。来写一下我做题经历吧:S组官方答案:ABACCCCBDACC......
  • CF1685E The Ultimate LIS Problem 题解
    LinkCF1685ETheUltimateLISProblem题意概述给定长为\(2n+1\)的排列,对于\(m\)次交换操作,求出每次操作后一个能使排列\(\text{LIS}\leqn\)的循环移位\(k\),......
  • 题解 [ABC259Ex] Yet Another Path Counting
    首先,每种颜色互不干扰,因此考虑对每种颜色统计答案。有两种解法:枚举起始格子\((x,y)\)和结尾格子\((z,w)\),由组合数易知共有\(\binom{z-x+w-y}{z-x}\)种路径。时间......
  • Codeforces-1753B Factorial Divisibility题解
    Codeforces-1753BFactorialDivisibility参考:https://blog.csdn.net/qq_38236082/article/details/127500190题意:问$a_1!+a_2!+a_3!+...+a_n!$能否被$x!$整除。......
  • 题解 [AGC044C] Strange Dance
    这道题启示我们Trie树是可以支持全局下标加\(1\)的。首先把\(3^n\)个人从低位到高位建到三进制Trie树上。类似二叉树的左右儿子,我们称由\(0,1,2\)边连接的儿子......
  • KMP 自动机,孤独的自动机(同时也是CF1721E的题解)
    给定字符串\(s\),以及\(q\)个串\(t_i\),求将\(s\)分别与每个\(t_i\)拼接起来后,最靠右的\(|t_i|\)个前缀的border长度。询问间相互独立。\(|s|\leq10^6,q......
  • 【题解】洛谷P2725 [USACO3.1]邮票 Stamps
    从n种邮票中选出不超过k张邮票,使选出来的邮票可以表示1~m之间(含)的所有数。每张邮票在不超过k的前提下,都可以使用无数次,因此可以将问题看成一个完全背包问题。n种邮票就是n......
  • [ARC069F]Flags 题解
    因为一个小错误整整调了一天qwq除去2-SAT部分没学过去学了一下,其它部分都想出来了,四舍五入我自己写了一道Cu,好欸(虽然这Cu好像非常水QAQ)。F-Flags一条数轴上有......
  • P8813(CSP-J2022T1)题解
    题意:算a ^ b,如果结果超出1e9就输出-1,反之输出结果。思路:边算边判加特判。代码:#include<cstdio>#definelllonglong#definemx1e9//边界usingnamespacestd;i......
  • P8814(CSP-J2022T2)题解
    题意:给定一个正整数 k,有k 次询问,每次给定三个正整数 n, e, d,求两个正整数 p, q,使 n = p × q且e × d =(p −1)×(q −1)+1。思路:通过题意可以发......