*题解:P7972 [KSN2021] Self Permutation

题目链接

解析

\(f_i\) 表示考虑前 \(i\) 个数,最终序列中含有 \(a_i\) 的序列个数。

对于转移,考虑最终序列中 \(a_i\) 左侧与谁相邻,从这些位置中转移。根据题意,如果在最终序列中,\(a_j\)\(a_i\) 左侧且相邻,那么就需要删掉下标在 \([j + 1,i - 1]\) 之间的元素。这些元素可以被删干净,当且仅当 \(\min(a_i,a_j) < \min_{k=j + 1}^{i - 1}a_k\)。现在问题变为对于每个 \(i\) 快速求出所有合法的 \(j\) 对应 \(f\) 之和。

考虑 \(a_i\) 最左能删多远,显然直到左侧第一个比 \(a_i\) 小的 \(a_l\) 都是可以删的。也就是说对于所有 \(j\in [l,i - 1]\)\(f_j\) 可以对 \(f_i\) 做贡献。考虑 \(a_j\) 最右能删多远,同理,应当能一直删直到右侧第一个比 \(a_j\) 小的位置 \(r_j\)。这样,对于所有可以转移到 \(i\)\(j\),若 \(a_i < a_j\),那么 \(j \in [l + 1,i - 1]\);若 \(a_i > a_j\),那么 \(r_j > i\)。可以发现这样得出的两种 \(j\) 是不交的。因此,可以使用单调栈求出每个元素左右侧第一个比它小的位置,再利用差分处理 \(a_j < a_i\) 时需要对 \([j + 1,r_j - 1]\) 做的贡献,最后用树状数组维护后缀 \(f\) 之和即可。

需要判一下可以把左边全部删完的情况。

为了统计答案,我们还需要处理出可能作为结尾出现的元素位置,可以发现,这些位置上的元素为后缀最小值。

时间复杂度 \(O(n\log n)\)

代码

/*
*/
#include <bits/stdc++.h>
#define eps 0.0000000001
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1) 
#define mid ((l + r) >> 1)
using namespace std;
typedef long long ll;
typedef unsigned ui;
typedef pair<ll, ll> pii;
const int N = 3e5 + 5, M = 20, P = 450, mod = 1e9 + 7, mod2 = 1e9 + 7, b1 = 131;
int a[N],f[N];
int l[N],r[N],b[N],d[N];
void add(int x,int k){
	for(;x;x -= x & -x){
		b[x] = (b[x] + k) % mod;
	}
}
int ask(int x){
	int res = 0;
	for(;x < N;x += x & -x){
		res = (res + b[x]) % mod;
	}
	return res;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int n;
	cin>>n;
	vector<int> v;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		while(!v.empty() && a[v.back()] > a[i]){
			r[v.back()] = i;
			v.pop_back();
		}
		v.push_back(i); 
	}
	while(!v.empty()){
		r[v.back()] = n + 1;
		v.pop_back();
	}
	for(int i=n;i>=1;i--){
		while(!v.empty() && a[v.back()] > a[i]){
			l[v.back()] = i;
			v.pop_back();
		}
		v.push_back(i); 
	}
	while(!v.empty()){
		l[v.back()] = 0;
		v.pop_back();	
	}
	for(int i=1;i<=n;i++){	
		d[i] = (d[i] + d[i - 1]) % mod;
		int x = ask(l[i] + 1);
		if(l[i] == 0) f[i]++;
		f[i] = (f[i] + d[i] + x) % mod;
		d[i + 1] = (d[i + 1] + f[i]) % mod;
		d[r[i]] = (d[r[i]] + mod - f[i]) % mod;
		add(i,f[i]);
	}
	int res = 0;
	for(int i=1;i<=n;i++){
		if(r[i] == n + 1){
			res = (res + f[i]) % mod;
		}
	}
	cout<<res;
	return 0;
}
posted @ 2026-08-05 17:36  yutar  阅读(1)  评论(0)    收藏  举报