CF2234E 思路分享(笛卡尔树,组合数学)
https://codeforces.com/problemset/problem/2234/E
题意概述
对于一个排列 \(p\),对于它每个位置 \(i\),定义 \(a_i\) 为满足:
- \(1\le l \le i \le r \le n\)
- \(\min(p_l,p_{l+1},\cdots,p_r) = p_i\)
的 \((l,r)\) 对数.
现给定长度为 \(n\) 的序列 \(a\),求能构成 \(a\) 序列的排列 \(p\) 的数量,模 \(10^9+7\).
思路
考虑构建笛卡尔树,\(a_i\) 为(\(i\) 的左子树大小 \(+1\))\(\cdot\)(\(i\) 的右子树大小 \(+1\)).
对于位置 \(1\),它没有左孩子,因此右子树的大小可以确定.
因为笛卡尔树子树内序号是连续的,因此 \(1\) 的父节点 \(f\) 可以确定.
然后跳到 \(f\),因为已知 \(1\) 的子树大小,即 \(f\) 的左子树大小,和上述对 \(1\) 的讨论是一致的.
对于每个位置的右子树,进入右子树的序号区间,最左端的位置它的左子树为空,和上面讨论是一致的.
因此可以递归地把笛卡尔树构建出来.
然后考虑如何计数.
对于每个节点 \(u\),\(p_u\) 只能填没选的数中最小的,剩余的数分配给左右子树即可,因此贡献为
\[\binom{sz[left[u]]+sz[right[u]]}{sz[left[u]]}
\]
最终答案为所有贡献的乘积.
时间复杂度 \(\mathcal{O}(n)\).
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
class inverse{
public:
int n,MOD;
vector<ll> fac,inv;
ll qpow(ll a,ll b){
ll res = 1;
while (b){
if (b&1) res = res*a%MOD;
a = a*a%MOD;
b >>= 1;
}
return res;
}
inverse(int _n,int _MOD){
n = _n;
MOD = _MOD;
fac = inv = vector<ll>(n+1);
fac[0] = 1;
for (int i=1;i<=n;i++){
fac[i] = fac[i-1]*i%MOD;
}
inv[n] = qpow(fac[n],MOD-2);
for (int i=n-1;i>=0;i--){
inv[i] = inv[i+1]*(i+1)%MOD;
}
}
ll C(ll a,ll b){
if (a<b || a<0 || b<0) return 0;
return fac[a]*inv[b]%MOD*inv[a-b]%MOD;
}
ll A(ll a,ll b){
if (a<b || a<0 || b<0) return 0;
return fac[a]*inv[a-b]%MOD;
}
ll cal_inv(ll p){
return qpow(p,MOD-2);
}
};
const int MOD = 1e9+7;
inverse inv(5e5,MOD);
void solve(){
int n;
cin >> n;
vector<ll> a(n+1);
for (int i=1;i<=n;i++){
cin >> a[i];
}
vector<int> left(n+1,-1),right(n+1,-1);
vector<int> sz(n+1);
bool ok = true;
function<int(int,int)> build = [&](int l,int r){
if (l<1 || l>n || r<1 || r>n || l>r || !ok) return -1;
if (a[l]%(left[l]!=-1?sz[left[l]]+1:1)){
ok = false;
return -1;
}
ll temp = a[l]/(left[l]!=-1?sz[left[l]]+1:1)-1;
sz[l] = 1+(left[l]!=-1?sz[left[l]]:0)+temp;
ll next = l+1+temp;
if (next>r+1){
ok = false;
return -1;
}
right[l] = build(l+1,next-1);
if (!ok) return -1;
if (next==r+1){
return l;
}
left[next] = l;
return build(next,r);
};
int head = build(1,n);
if (!ok){
cout << 0 << '\n';
return;
}
ll res = 1;
function<void(int)> dfs = [&](int u){
res = res*inv.C((left[u]!=-1?sz[left[u]]:0)+(right[u]!=-1?sz[right[u]]:0),left[u]!=-1?sz[left[u]]:0)%MOD;
if (left[u]!=-1){
dfs(left[u]);
}
if (right[u]!=-1){
dfs(right[u]);
}
};
dfs(head);
cout << res << '\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号