📅  最后修改于: 2023-12-03 15:42:17.117000             🧑  作者: Mango
给出一个长度为 $n$ 的正整数序列 $A$,你需要计算有多少个非空子序列 $B$ 满足:
第一行两个整数 $n, k$。
第二行 $n$ 个整数 $A_1,A_2,...,A_n$。
输出一个整数,表示满足条件的子序列个数模 $10^9+7$ 的值。
输入:
5 5
1 2 3 2 1
输出:
12
考虑如何快速地求出子序列个数。我们可以对于每一个 $A_i$,统计以它为最小值的子序列个数 $f_i$。容易发现,以 $A_i$ 为最小值的子序列,必须要包含 $A_i$。然后,我们依次插入 $A_1, A_2, ..., A_n$,并且在插入 $A_i$ 时,将 $[1, i-1]$ 中所有比 $A_i$ 大的数加入个数值 $f_i$ 中,这样就可以统计以 $A_i$ 为最小值的子序列个数了。具体来说,对于每个 $A_i$,都可以找到一个位置 $p_i$,使得 $A_{p_i}=\min{A_{j}, j\in[1,i]}$,那么以 $A_i$ 为最小值的子序列个数就等于:
$$ f_i=\begin{cases} (p_{i}-1)+1 & \text{如果 $i=1$ 或者所有 $A_j<j(i)$ 都不是 $A_i$ 的前驱}\ (p_{i}-p_{j_{i-1}})f_{j_{i-1}} & \text{如果 $j_{i-1}=\max{j, A_j<A_i}$ 存在} \end{cases} $$
现在,我们已经知道了如何计算 $f_i$,然而我们并不知道如何求出满足条件的子序列数。
注意到一个子序列 $B$ 的最大值与最小值之和,等于该子序列最大值与次大值之和、次大值与第三大值之和、$\cdots$、最小值与次小值之和的总和。因此,我们可以对于每一个 $A_i$,统计以 $A_i$ 为最大值的子序列个数 $g_i$。统计方式与 $f_i$ 类似,只不过是所有比 $A_i$ 小的数加入个数值 $g_i$ 中。
考虑枚举最大值和次小值的和 $s$,以及最小值的位置 $l$ 和次大值的位置 $r$。我们需要满足如下条件:
对于满足条件的位置 $(l,i,r)$,存在 $f_i\times g_r$ 个方案。
这个枚举过程可以通过维护两个单调栈来完成,从而时间复杂度可以做到 $\mathcal{O}(n)$。具体实现细节可以参考代码。ctime复杂度:$O(n \log{n} )$.
我采用C++来实现。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int max_n=1e5+7;
int n,k;
int a[max_n];
int f[max_n],g[max_n];
void init(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
}
void solve(){
stack<int> stk_g;
for(int i=1;i<=n;i++){
g[i]=1;
while(!stk_g.empty() && a[stk_g.top()]>a[i]){
(g[i]+=g[stk_g.top()])%=mod;
stk_g.pop();
}
stk_g.push(i);
}
stack<int> stk_f;
int ans=0;
for(int i=n;i>=1;i--){
while(!stk_f.empty() && a[stk_f.top()]>=a[i]){
stk_f.pop();
}
if(stk_f.empty()){
f[i]=i;
}else{
f[i]=stk_f.top();
}
stk_f.push(i);
if(a[i]==a[1] || a[i]==a[f[i]+1]){
continue;
}
int s=k-a[i];
if(s<a[f[i]+1] || a[i]<a[f[i]+1]){
continue;
}
int p=f[i]+1;
while(p<i && a[p]<=s-a[i]){
p=f[p]+1;
}
if(p==i){
continue;
}
int q=i-1;
while(q>p && a[q]>=s-a[i]){
q=f[q]-1;
}
if(a[q]!=s-a[i]){
continue;
}
(ans+=(ll)f[i]*g[q]%mod)%=mod;
}
printf("%d\n",ans);
}
int main(){
init();
solve();
return 0;
}