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

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

时间:2024-10-31 23:19:36浏览次数:7  
标签:std 洛谷 int 题解 inv return i64 ZJOI2010 fac

题目链接

[ZJOI2010] 排列计数 - 洛谷


题解

看到 \(p_i > p_{\lfloor i/2 \rfloor}\) 这个条件,可能一开始不会有什么想法。但是如果我们换种写法, 即: \(p_i<p_{2i} \land p_i<p_{2i+1}\)。这样我们就能很容易看出来,这是小根堆的形式。

现在我们从根节点开始考虑,假设左子树的大小为 \(a\),右子树的大小为 \(b\),那么根节点的选择显然只有一种,剩下由其子树内部分配,其中这一步的方案数为 \(C_{a+b}^a\),即把剩下的数选 \(a\) 个分给左子树,剩下的给右子树。由于每一层的决策都是独立的,所以最后的答案就是这些相乘。

值得注意的是,这题可能存在 \(n>m\) 的情况,即 \(n\) 可能是 \(m\) 的倍数,这种情况下没办法正常的维护逆元,所以需要用到 \(Lucas\)定理。


代码

#include <bits/stdc++.h>

using i64 = long long;

struct Lucas
{
	int p;
	std::vector<i64> fac, inv;

	Lucas() {}
	Lucas(int n)
	{
		init(n);
	}

	void init(int n)
	{
		p = n;
		fac.assign(n, 1);
		inv.assign(n, 1);

		for(int i = 1; i < n; i++) fac[i] = fac[i - 1] * i % n;

		for(int i = 2; i < n; i++) inv[i] = (n - n / i) * inv[n % i] % n;
		for(int i = 2; i < n; i++) inv[i] = inv[i] * inv[i - 1] % n;
	}

	i64 binom(int n, int m)
	{
		if(m > n || m < 0) return 0;
		return fac[n] * inv[m] % p * inv[n - m] % p;
	}

	i64 cacl(int n, int m)
	{
		i64 ans = 1;
		while(n || m)
		{
			ans = ans * binom(n % p, m % p) % p;
			n /= p, m /= p;
		}

		return ans;
	}
};

int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int n, P; std::cin >> n >> P;

	Lucas comb;
	if(n >= P) comb.init(P);

	i64 ans = 1;

	std::vector<i64> fac(n + 1, 1), inv(n + 1, 1);
	for(int i = 2; i <= n; i++) fac[i] = fac[i - 1] * i % P;
	for(int i = 2; i <= n; i++) inv[i] = (P - P / i) * inv[P % i] % P;
	for(int i = 2; i <= n; i++) inv[i] = inv[i] * inv[i - 1] % P;

	auto C = [&](int n, int m) -> i64
	{
		return fac[n] * inv[m] % P * inv[n - m] % P;
	};

	auto dfs = [&](auto dfs, int p) -> int
	{
		if(p > n) return 0;
		int a = dfs(dfs, p << 1);
		int b = dfs(dfs, p << 1 | 1);

		if(n < P) ans = ans * C(a + b, a) % P;
		else ans = ans * comb.cacl(a + b, a) % P;
		return a + b + 1;
	};	
	dfs(dfs, 1);

	std::cout << ans << "\n";

	return 0;
}

标签:std,洛谷,int,题解,inv,return,i64,ZJOI2010,fac
From: https://www.cnblogs.com/Repeater11/p/18519146

相关文章

  • 洛谷B2064
    B2064斐波那契数列-洛谷|计算机科学教育新生态斐波那契数列题目描述斐波那契数列是指这样的数列:数列的第一个和第二个数都为$1$,接下来每个数都等于前面$2$个数之和。给出一个正整数$a$,要求斐波那契数列中第$a$个数是多少。输入格式第1行是测试数据的组数n,后面......
  • P1779 魔鬼杀手 题解&&思路
    P1779魔鬼杀手题解&&思路题目链接。分析题目性质我们发现假如有状态表示\(M\)个方案选或不选,那么这个状态有唯一确定的结果,即结果不会随着施法的顺序而改变。考虑\(dp.\)我们从题目出发,发现每个方案有单个攻击或者集体攻击,想一想从这个方面考虑。又由于每一个方案是可......
  • 洛谷Python顺序结构题解合集
    P5705【深基2.例7】数字反转a=s[0]b=s[1]c=s[2]d=s[4]print(f"{d}.{c}{b}{a}")P5706【深基2.例8】再分肥宅水ans=float(a[0])/int(a[1])beizi=2*int(a[1])print(f"{ans:.3f}\n{beizi}")P5708【深基2.习2】三角形面积p=0.5*(a+b+c)ans=pow((p*(p-a)*(p-b)*(p-c)),0.5......
  • Navicat 连接 MySQL 失败:2002-can‘t connect to server on localhost(10061)问题解决
    连接不上问题可能有如下原因服务器安全组中没有配置3306端口mysql服务端口只开放本地了如下:修改/etc/mysql/mysql.conf.d/mysqld.cnf中bind-address和mysqlx-bind-address注释掉重启mysql服务systemctlrestartmysqlmysql登录用户的host为localhost只允......
  • CATIA许可证常见问题解答
    在使用CATIA软件的过程中,许可证问题常常是用户关心的焦点。为了帮助大家更好地理解和解决这些问题,我们整理了一份CATIA许可证常见问题解答,希望能为您提供便捷的参考。问题一:如何激活CATIA许可证?解答:激活CATIA许可证通常需要访问软件的官方平台或使用特定的许可证管理工具。您需......
  • 20241031模拟赛题解
    T1题目描述给定一个圆形蛋糕,被\(n\)条切割线分成\(n\)个扇形蛋糕块,按照顺时针编号,第\(i\)块上有\(a_i\)个草莓,第\(i\)条切割线到第\(i+1\)条切割线之间的部分是第\(i\)块蛋糕。Alice和Bob流选择切割线,假设Alice选择了第\(i\)条切割线,Bob选择了第\(j\)条......
  • POI2011/洛谷P3523 DYN-Dynamite
    前言Link本来一个很直观的题面,非要搞形式化题意反而使题意变得非常迷惑。题意有一栋树形建筑,其中有一些点摆放了TNT,树边上都摆放了引信,引信的燃烧时间为\(1\)秒\(/\)边,现在你要选择\(m\)个点同时点燃引信(起爆),则显然TNT被引爆的时间为到离它最近的起爆处的距离,请你求......
  • POI2011/洛谷P3514 LIZ-Lollipop
    前言典中典思维蓝题难度薄纱模板水紫捏。\(1\)\(2\)序列这种也不是第一次见了,感觉多多少少都沾点Ad-hoc。话说这种考法真的好吗,一上来就是一个门槛很高的性质,推出来就满分,推不出来就\(0\)分,正推和反推的难度完全不是一个思维量级。题意Link给一个只有\(1\)和\(2\)......
  • Codeforces Global Round 27,D. Yet Another Real Number Problem 题解
    单调栈+贪心题意:给定一个序列从左往右对于每个索引iii,求出其前缀的数组可以进行任意次以下操作的条件下:选择......
  • 2024CCPC哈尔滨部分题解
    赛时被评测机卡死了M.奇怪的上取整求\(\sum_{i=1}^{n}f(n,i)\)\(Input\)第一行一个整数\(T(1<=T<=10^3)\),表示数据组数对于每组数据,一行一个整数\(n(1<=n<=10^9)\)\(Output\)对于每组数据,输出一行一个整数,表示答案。\(Sample\)35451114514————————21T10......