At406 C - ~

C. ~


原题链接

题意概述

给定一个长为n的序列,求解其连续子序列的个数,连续子序列满足以下要求
1.a[1]<a[2]
2.存在a[i]>a[i-1]且a[i]>a[i+1],且仅存在一个位置满足
3.存在a[i]<a[i-1]且a[i]<a[i+1],且仅存在一个位置满足

解题思路1

由于峰值和谷值是对于同一个数组同步辩护的,所以对于原数组每一个位置峰值和谷值可以利用pair绑定记录,不难观察性质得到合法的长度一定是大于等于4的,可以利用双指针维护左端点更新峰值和谷值,右端点记录已经更新的左端点可以与多少值形成一个存在且仅存在一对峰值和谷值的区间

AC code1

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
int main(){
    cin.tie(0)->ios::sync_with_stdio(false);
    int n;cin>>n;
    vector<int>a(n+1);
    for(int i=1;i<=n;i++) cin>>a[i];
    vector<ll>pre_min(n+1);//记录谷值
    vector<ll>pre_max(n+1);//记录峰值
    for(int i=2;i<n;i++){
        if(a[i]>a[i+1]&&a[i]>a[i-1]) pre_max[i]=1;
        if(a[i]<a[i+1]&&a[i]<a[i-1]) pre_min[i]=1;
    }
    for(int i=1;i<=n;i++){
        pre_max[i]+=pre_max[i-1];
        pre_min[i]+=pre_min[i-1];
    }
    ll ans=0;
    map<pair<ll,ll>,ll>mp;
    for(int r=4;r<=n;r++){
        int l=r-3;
        if(a[l]<a[l+1]) mp[{pre_max[l],pre_min[l]}]++;//只有a[1]<a[2]的时候左端点合法
        ans+=mp[{pre_max[r-1]-1,pre_min[r-1]-1}];
    }
    cout<<ans<<endl;
    return 0;
}

解题思路2

因为子数组中a[1]一定小于a[2],那么结果就是要统计形如\(1 3 2 4\)这样先上升后下降的子数组的个数,对于每一位i,如果a[i]<a[i+1]记作1,否则记作0,那我们要维护的问题就转化为了对于一个01串维护以0开头的111.1100..0011..11这样区块的数量,由于我们不在乎中间有多少个0,只要前面与后面是1一定满足上升下降上升的趋势,所以对于这种区间个数统计可以独立统计极大区块前缀连续1和后缀连续1的乘积。

AC code2

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
int main(){
    cin.tie(0)->ios::sync_with_stdio(false);
    int n;cin>>n;
    vector<int>a(n+2);
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++){
        if(a[i]<a[i+1]){
            a[i]=1;
        }
        else a[i]=0;
    }
    ll ans=0,k=0;
    vector<int>res;
    for(int i=1;i<=n;i++){
        if(a[i]) k++;
        else{
            if(k) res.push_back(k);
            k=0;
        }
    }
    for(int i=0;i<(int)res.size()-1;i++) ans+=(ll)res[i]*res[i+1];
    cout<<ans<<endl;
    return 0;
}

小结

PS:两种思路都挺常见的,没想出来真的很糖啊

posted @ 2025-05-18 00:55  usedchang  阅读(24)  评论(0)    收藏  举报