*题解: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;
}
posted @ 2026-08-27 20:35  yutar  阅读(4)  评论(0)    收藏  举报