首页 > 其他分享 >「题解报告」[POI2008]PER-Permutation

「题解报告」[POI2008]PER-Permutation

时间:2022-11-05 20:23:25浏览次数:108  
标签:cnt frac POI2008 题解 ll ans Permutation prod sum

「题解报告」[POI2008]PER-Permutation

点击查看目录

目录

不理解哪里难了,学过扩卢并且推一下式子基本就是两眼切吧。

个人感觉顶多上位紫。

思路

首先设 \(f_i\) 表示前 \(i-1\) 位固定,第 \(i\) 位选一个比原来小的,后面随便排的方案数。

显然 \((\sum_{i=1}^{n}f_i)+1\) 为答案,那么考虑如何快速求出 \(f_i\)。

考虑用“交换”的思想,即在后 \(n-i\) 个数中找到比 \(a_i\) 小的数和它换一下,然后再随便排。

然而这里是可重集,所以还要去重乘上 \(\dfrac{1}{\prod_{j}(A_{cnt_j}^{cnt_j})}=\dfrac{1}{\prod_{j}(cnt_j!)}\)。(\(cnt_j\) 表示在 \(i\sim n\) 中 \(j\) 出现了几次)

那么可写出式子:

\[f_i=\frac{(n-i)!}{\prod(cnt_j!)}\sum_{j=1}^{n}[a_i>a_j]\bmod M \]

但是 \(M\) 不是质数,没法直接求逆元,怎么办呢?

用扩展卢卡斯的思想

扩卢是啥?

首先把 \(M\) 分解成 \(p_1^{k_1}p_2^{k_2}\cdots p_t^{k_t}(p_i\in\mathbb{P})\),解决这个方程组:

\[\begin{cases} x \equiv \frac{(n-i)!}{\prod(cnt_j!)}\sum_{j=1}^{n}[a_i>a_j] \pmod{p_1^{k_1}}\\ x \equiv \frac{(n-i)!}{\prod(cnt_j!)}\sum_{j=1}^{n}[a_i>a_j] \pmod{p_2^{k_2}}\\ \dots\\ x \equiv \frac{(n-i)!}{\prod(cnt_j!)}\sum_{j=1}^{n}[a_i>a_j] \pmod{p_t^{k_t}}\\ \end{cases} \]

这些内容可用 CRT 合并,然后注意这个方程:

\[x \equiv \frac{(n-i)!}{\prod(cnt_j!)}\sum_{j=1}^{n}[a_i>a_j] \pmod{p^k}\\ \]

\(\sum_{j=1}^{n}[a_i>a_j]\) 简单树状数组解决,问题就只剩下了如何求 \(\dfrac{(n-i)!}{\prod(cnt_j!)}\bmod{p^k}\)。

继续用扩卢的思想,把分子和分母中的 \(p\) 提取出来

\[\large\dfrac{\frac{(n-i)!}{p^{x}}}{\frac{\prod(cnt_j!)}{p^{y}}}*p^{x-y}\bmod{p^k} \]

直接提显然会 TLE,所以我们充分发挥人类智慧:求 \(f_i\) 时倒序跑,这样每次只用额外提出 \((n-i)\) 和 \(cnt_{a_i}\) 的 \(p\),然后与上一次的分子与分母合并即可

然后有个小细节:\(x-y\) 可能为负,此时要发挥人类智慧用逆元淦。

然后就切掉了。

代码

点击查看代码
#include <bits/stdc++.h>
#define lowb(a, r, x) lower_bound(a + 1, a + r + 1, x) - a
#define uppb(a, r, x) upper_bound(a + 1, a + r + 1, x) - a
#define _for(i, a, b) for (ll i = a; i <= b; ++i)
#define for_(i, a, b) for (ll i = a; i >= b; --i)
#define far(i, vec) for (auto i : vec)
#define bdmd int mid = (l + r) >> 1
#define NO nullptr
typedef long double ldb;
typedef long long ll;
typedef double db;
const ll N = 3e5 + 10, INF = 1ll << 40;

class TreeArray {
public:
	ll b[N], mx;
	inline ll lowbit (ll x) { return x & -x; }
	inline void Update (ll x) {
		while (x <= mx) ++b[x], x += lowbit (x);
		return;
	}
	inline ll Query (ll x) {
		ll ans = 0;
		while (x > 0) ans += b[x], x -= lowbit (x);
		return ans;
	}
} ta;

namespace MathBasic {
	inline void GetFactor (ll x, std::vector <ll>& p, std::vector <ll>& k) {
		p.push_back (0), k.push_back (0);
		for (ll i = 2; i * i <= x; ++i) {
			if (!(x % i)) {
				p.push_back (i), k.push_back (0);
				while (!(x % i)) x /= i, ++k[k.size () - 1];
			}
		}
		if (x != 1) p.push_back (x), k.push_back (1);
		return;
	}
	inline ll ExGcd (ll a, ll b, ll& x, ll& y) {
		if (!b) { x = 1, y = 0; return a; }
		ll g = ExGcd (b, a % b, x, y), _x = x;
		x = y, y = _x - (a / b) * y;
		return g;
	}
	inline ll Inv (ll a, ll P) {
		ll x, y;
		ExGcd (a, P, x, y);
		return (x % P + P) % P;
	}
	inline ll FastPow (ll a, ll b, ll Mod = INF) {
		ll ans = 1;
		if (b < 0) a = Inv (a, Mod), b = -b;
		while (b) {
			if (b & 1) ans = ans * a % Mod;
			a = a * a % Mod, b >>= 1;
		}
		return ans;
	}
}

namespace CRT {
	using namespace MathBasic;
	ll a[N], m[N], fac[N], fm[N], in[N], cnt[N];
	std::vector <ll> p, k;
	inline void Pre (ll P) {
		GetFactor (P, p, k);
		ll len = p.size () - 1;
		_for (i, 1, len) fac[i] = fm[i] = 1;
		return;
	}
	inline ll Idx (ll x, ll P) {
		ll idx = 0;
		while (!(x % P)) x /= P, ++idx;
		return idx;
	}
	inline ll DelP (ll x, ll P) {
		while (!(x % P)) x /= P;
		return x;
	}
	inline ll CRT (ll n, ll j, ll ai, ll P) {
		ll ans = 0, len = p.size () - 1;
		++cnt[ai];
		_for (i, 1, len) {
			m[i] = FastPow (p[i], k[i]);
			in[i] += Idx ((n - j) ? (n - j) : 1, p[i]) - Idx (cnt[ai], p[i]);
			fac[i] = fac[i] * DelP ((n - j) ? (n - j) : 1, p[i]) % m[i];
			fm[i] = fm[i] * DelP (cnt[ai], p[i]) % m[i];
			a[i] = FastPow (p[i], in[i], m[i]) * fac[i] % m[i] * Inv (fm[i], m[i]) % m[i];
			ll q = P / m[i];
			ans = (ans + a[i] * q % P * Inv (q, m[i]) % P) % P;
		}
		return ans;
	}
}

namespace SOLVE {
	ll n, P, a[N], ans = 1;
	inline ll rnt () {
		ll x = 0, w = 1; char c = getchar ();
		while (!isdigit (c)) { if (c == '-') w = -1; c = getchar (); }
		while (isdigit (c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar ();
		return x * w;
	}
	inline void In () {
		n = rnt (), P = rnt ();
		_for (i, 1, n) a[i] = rnt (), ta.mx = std::max (ta.mx, a[i]);
		return;
	}
	inline void Solve () {
		CRT::Pre (P);
		for_ (i, n, 1) {
			ll crt = CRT::CRT (n, i, a[i], P);
			ans = (ans + ta.Query (a[i] - 1) % P * crt % P) % P;
			ta.Update (a[i]);
		}
		return;
	}
	inline void Out () {
		printf ("%lld\n", ans);
		return;
	}
}
int main () {
#ifndef ONLINE_JUDGE
	freopen ("date.in", "r", stdin);
#endif
	SOLVE::In ();
	SOLVE::Solve ();
	SOLVE::Out ();
	return 0;
} /*



*/

标签:cnt,frac,POI2008,题解,ll,ans,Permutation,prod,sum
From: https://www.cnblogs.com/Keven-He/p/solution-P3477.html

相关文章

  • Luogu P5816[CQOI2010]内部白点题解
    LinkLuoguP5816Description一个平面直角坐标系内有\(n\)个黑点,其余点为白点,将会进行若干次变换,每次变换会把上下左右方向都有黑点的白点变成黑点,直到找不到符合要求......
  • python print 打印延迟问题解决
    转载:https://wenku.baidu.com/view/ffc89347bb4ae45c3b3567ec102de2bd9705de56.html?wkts=1667639107060&bdQuery=python+print%E7%AB%8B%E5%8D%B3%E6%89%93%E5%8D%B0......
  • 【题解】洛谷P2946 [USACO09MAR]Cow Frisbee Team S
    这题乍一看是一个朴素的01背包问题,将所有方案的集合按照能力值的和来划分成1~m,然后把m当作体积,把n当作物品数,做一个01背包的代码。于是我兴冲冲地写了代码交上去,然后十组样......
  • CCPC 2022桂林 J.Permutation Puzzle
    模拟赛的时候这道题细节写挂了,硬是调不出来。。。首先想到拓补排序。然后可以发现,正反各跑一次可以获得每个点的取值范围,即上界和下界。(特殊地,对于已知点,其上下界相等且等......
  • accoders NOI #5047. 猜数游戏 题解
    题目描述Alice和Bob玩游戏。Alice有一个\(1~n\)中的正整数\(y\)。Bob不知道这个数。游戏中的每一轮,Bob选一个正整数\(x\),并提问Alice:\(y\)是否大于等于\(x......
  • 「题解」Codeforces 1612F Armor and Weapons
    首先可以不管套件,假定\(n<m\),那么答案不超过\(\mathcal{O}(\logn+\frac{m}{n})\),也就是先倍增把\(n\)造出来,然后一步步造\(m\).答案这么小,那么常见的套路就是把答案......
  • 问题解决:vscode运行python找不到文件
    问题描述:使用VSCode执行Python代码调用其他文件时报FileNotFound错误,终于发现是VSCode工作路径默认是当前文件所在工作区的根目录,而不是当前文件所在目录。发生条件:根......
  • 「题解」牛客练习赛105 F 胖头鱼头胖
    先对每个位置\(i\)对集合幂级数\(x^0+x^1+\cdots+x+x^{a_i}\)FWT,那么询问就是将区间里面所有FWT后的集合幂级数作点积再IFWT后提取\(x^s\)的系数。首先可以通......
  • P2484 [SDOI2011]打地鼠 题解
    P2484[SDOI2011]打地鼠题解目录P2484[SDOI2011]打地鼠题解题目分析思路代码题目P2484[SDOI2011]打地鼠题解分析锤子每次只能将每个洞里打掉一只地鼠,所以对于每......
  • P7365 [CTSC2002]颁奖典礼 题解
    一道神奇的有关网格的DP(一些大佬称其为暴力DP)。这里将“I”字母分为的三个部分称为第一,二,三部分。做法:设fi,j,k,l(l∈[1,3])表示第i行,当前在第l部分,区间[j,k]的......