P11159 【MX-X6-T5】 再生
题目背景
原题链接:https://oier.team/problems/X6F。
このまま$\$
らったった$\$
音に乗って$\$
今きっと世界で僕だけだ$\$
後ろ向きな歌を聴いて$\$
少しだけ$\$
前向きに生きていく
破碎的点依照破碎的规则进行重组,如此再生的一个结构将会是什么样的呢?
题目描述
现有一棵 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=v∈Sumaxhv+1。
给定数组 t 1 ⋯ n t_{1\cdots n} t1⋯n,你需要计算有多少种树满足:
- 对于根节点 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} t1⋯n,表示 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 1≤n≤5×105, 1 ≤ t i ≤ i 1\leq t_i\leq i 1≤ti≤i,保证取模前答案不为 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 n≤5。
- Subtask 3(17 pts): n ≤ 16 n\leq 16 n≤16。
- Subtask 4(22 pts): n ≤ 2 × 10 3 n\leq 2\times 10^3 n≤2×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考级编程题实现、白名单赛事考题实现,记录日常的编程生活、比赛心得,感兴趣的请关注,我后续将继续分享相关内容
用C++实现信奥题 P11159 【MX-X6-T5】 再生&spm=1001.2101.3001.5002&articleId=164287910&d=1&t=3&u=0cf43bf836a049408346ce33637f24b1)
2082

被折叠的 条评论
为什么被折叠?



