打卡信奥刷题(3546)用C++实现信奥题 P11159 【MX-X6-T5】 再生

P11159 【MX-X6-T5】 再生

题目背景

原题链接:https://oier.team/problems/X6F


このまま$\$
らったった$\$
音に乗って$\$
今きっと世界で僕だけだ$\$
後ろ向きな歌を聴いて$\$
少しだけ$\$
前向きに生きていく

—— 再生 - Nanatsukaze

破碎的点依照破碎的规则进行重组,如此再生的一个结构将会是什么样的呢?

题目描述

现有一棵 n n n 个点的有标号有根树,给定其长链剖分得到的 top 数组,请你输出有多少种不同的树可以在长链剖分之后得到该 top 数组。答案对 20051131 20051131 20051131(质数)取模。

具体来说,对于一棵树 T T T,对所有点 u u u 定义其树高 h u h_u hu

  • 如果 u u u 是叶子,则 h u = 1 h_u=1 hu=1
  • 否则设 u u u 的孩子集合为 S u S_u Su,则 h u = max ⁡ v ∈ S u h v + 1 h_u=\max\limits_{v\in S_u}h_v + 1 hu=vSumaxhv+1

给定数组 t 1 ⋯ n t_{1\cdots n} t1n,你需要计算有多少种树满足:

  • 对于根节点 r r r,满足 t r = r t_r=r tr=r
  • 对于每一个不是叶子的节点 u u u,存在恰好一个孩子 v v v 满足 h v + 1 = h u h_v+1=h_u hv+1=hu 并且 t v = t u t_v=t_u tv=tu,其他孩子满足 t v = v t_v=v tv=v

20051131 20051131 20051131(质数)。

两棵树不同当且仅当它们的根不同或它们的边集不同。

保证答案不为 0 \bf 0 0,但是不保证答案在模意义下不为 0 \bf 0 0

输入格式

第一行一个正整数 n n n

接下来一行, n n n 个空格分隔的正整数 t 1 ⋯ n t_{1\cdots n} t1n,表示 top 数组。

输出格式

一行一个整数表示答案对 20051131 20051131 20051131 取模的值。

输入输出样例 #1

输入 #1

5
1 1 1 4 4

输出 #1

2

输入输出样例 #2

输入 #2

16
1 2 1 4 1 4 1 4 9 1 1 12 1 1 12 1

输出 #2

7181107

说明/提示

【样例解释 #1】

仅有图中的两种树满足条件。

【数据范围】

对于所有数据,保证 1 ≤ n ≤ 5 × 10 5 1\leq n\leq 5\times 10^5 1n5×105 1 ≤ t i ≤ i 1\leq t_i\leq i 1tii,保证取模前答案不为 0 0 0

捆绑测试,共 5 个 Subtask,具体限制如下所示:

  • Subtask 1(11 pts): t i = 1 t_i=1 ti=1
  • Subtask 2(24 pts): n ≤ 5 n\leq 5 n5
  • Subtask 3(17 pts): n ≤ 16 n\leq 16 n16
  • Subtask 4(22 pts): n ≤ 2 × 10 3 n\leq 2\times 10^3 n2×103
  • Subtask 5(26 pts):无特殊限制。

C++实现

#include <bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&-(x))
#define mod 20051131
#define MAXN 10000005
using namespace std;
int n, k, t[MAXN], f[MAXN], ans;
map<int, int> mp;
vector<int> vec;
void update(int x) {
	while (x <= n) {
		t[x]++;
		x += lowbit(x);
	}
}
int query(int x) {
	int ans = 0;
	while (x) {
		ans += t[x];
		x ^= lowbit(x);
	}
	return ans;
}
signed main() {
	f[0] = 1;
	for (int i = 1; i <= 1000000; i++) {
		f[i] = f[i - 1] * i % mod;
	}
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> k;
		mp[k]++;
	}
	for (pair<int, int> i : mp) {
		vec.push_back(i.second);
	}
	sort(vec.begin(), vec.end());
	reverse(vec.begin(), vec.end());
	for (int i : vec) {
		if (ans == 0) {
			ans = f[i - 1];
			for (int j = 1; j <= i; j++) {
				update(j);
			}
			continue;
		}
		ans *= f[i - 1];
		ans %= mod;
		ans *= query(n) - query(i);
		ans %= mod;
		for (int j = 1; j <= i; j++) {
			update(j);
		}
	}
	cout << ans << endl;
	return 0;
}

在这里插入图片描述

后续

接下来我会不断用C++来实现信奥比赛中的算法题、GESP考级编程题实现、白名单赛事考题实现,记录日常的编程生活、比赛心得,感兴趣的请关注,我后续将继续分享相关内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值