*题解:P13037 [GCJ 2021 #2] Hidden Pancakes
解析
考虑从列表中能获取到的信息,发现我们可以确定最大煎饼所在位置,即序列中最右侧 \(1\) 的位置。确定最大值位置 \(x\) 之后,序列被分成了 \([1,x - 1]\) 和 \([x + 1,n]\) 两段,对这两段分别求解,记答案分别为 \(c_l\) 和 \(c_r\),那么这一整段的答案就为 \(c_l\cdot c_r\cdot \binom{n-1}{n-x}\),乘组合数是为了分配数值,由于煎饼的覆盖关系只取决于相对大小,所以可以任意分配。
分治时,若当前序列中最大煎饼位于最右侧值为 \(v\) 的地方,则对于分治后的右侧序列,最大煎饼位于最右侧值为 \(v + 1\) 的地方,对于左侧序列,最大煎饼仍位于最右侧值为 \(v\) 的地方。为了找最大煎饼的位置,可以将序列中所有位置按值分类,找的时候二分即可。
时间复杂度 \(O(n\log n)\)。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 1e5 + 5,M = 300000 + 5,K = 10000 + 5,mod = (int)1e9 + 7;
vector<int> pos[N];
int v[N];
int fac[N],inv[N];
int qmi(int a,int b){
int res = 1;
while(b){
if(b & 1) res = 1ll * res * a % mod;
a = 1ll * a * a % mod;
b >>= 1;
}
return res;
}
int C(int n,int m){
if(m > n) return 0;
return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;
}
int calc(int l,int r,int now){
if(l > r) return 1;
if(l == r) return v[l] == now;
auto it = upper_bound(pos[now].begin(),pos[now].end(),r);
if(it == pos[now].begin() || *prev(it) < l) return 0;
int x = *prev(it);
int res = 1ll * calc(l,x - 1,now) * calc(x + 1,r,now + 1) % mod * C(r - l,r - x) % mod;
return res;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
// freopen("in.txt","r",stdin);
// freopen("out1.txt","w",stdout);
fac[0] = 1;
for(int i=1;i<N;i++){
fac[i] = 1ll * fac[i - 1] * i % mod;
}
inv[N - 1] = qmi(fac[N - 1],mod - 2);
for(int i=N - 2;i>=0;i--){
inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
}
int T;
cin>>T;
int x = 0;
while(T--){
x++;
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>v[i];
pos[v[i]].push_back(i);
}
int res = calc(1,n,1);
cout<<"Case #"<<x<<": "<<res<<'\n';
for(int i=1;i<=n;i++){
pos[i].clear();
}
}
return 0;
}

浙公网安备 33010602011771号