📜  门| GATE-CS-2014-(Set-1) |第 50 题(1)

📅  最后修改于: 2023-12-03 15:42:17.117000             🧑  作者: Mango

题目描述

给出一个长度为 $n$ 的正整数序列 $A$,你需要计算有多少个非空子序列 $B$ 满足:

  1. $B$ 是 $A$ 的子序列。
  2. $B$ 中的最小值和最大值之和等于 $k$。
输入格式

第一行两个整数 $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$。我们需要满足如下条件:

  1. $s=k-A_l$。
  2. $A_r=s-A_i$,且 $A_i<A_r<A_l$。
  3. $B$ 以 $A_l$ 为最大值,以 $A_r$ 为次大值。
  4. $B$ 中 $A_i$ 与 $A_r$ 之间的所有元素均小于等于 $A_i$。

对于满足条件的位置 $(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;
}