CF2069C Beautiful Sequence 解题报告

· · 题解

题目传送门

题目大意

有一个只有 1、2、3 组成的序列 a,找漂亮子序列(对于非首元素,左边都有比它小的元素,非尾元素,右边都有比它大的元素)的个数,答案对 998244353 取模。

解题思路

对于只有 1、2、3 组成的序列,不难想出这个漂亮子序列的形式:首元素是 1,尾元素是 3,中间必须全是 2

对于每一个 1,遍历它后面的这个序列时,当它遇到 3 时,它和这个 3 对答案的贡献为 2^{cnt2}-1cnt2 为当前 1 和当前 3 之间的 2 的个数)。则这个 1 对答案的总贡献为

\sum_{i=1}^{n}[a_i=1]\sum_{j=i+1}^{n}[a_j=3](2^{cnt2_{i,j}}-1)

其中 [bool] 为这个布尔表达式的值。

显然这个式子是需要 n^2 的复杂度实现的,如何简化时间复杂度呢?

考虑将第一个 1 所贡献的答案求出为 ans1,在遍历时想要在 ans1 的基础上进行运算得出后面的 1 对答案的贡献:

预处理求出 ans1cnt3 的时间复杂度为 O(n),遍历序列的复杂度为 O(n\log m),其中 m 为模数 998244353

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

AC Code

#include<bits/stdc++.h>
#define int long long
#define mod 998244353
using namespace std;
int n;
int a[200005]; 
int re()//快读 
void wr(int x)//快写 
int ksm(int a,int b)//快速幂 
{
    int ans=1;
    for(;b;a*=a,a%=mod,b>>=1)
        if(b&1)
            ans*=a,ans%=mod;
    return ans;
}
signed main(){
    int T=re();
    while (T--)
    {
        int n=re(),ans=0;
        for(int i=1;i<=n;i++)
            a[i]=re();
        int cnt2=0,cnt3=0;
        int ans1=0,pos=n;//pos为第一个1的位置 
        for(int i=1;i<=n;i++)
            if(a[i]==1)
            {
                pos=i;
                break;
            }
        for(int i=pos;i<=n;i++)
            if(a[i]==2)
                cnt2++;
            else if(a[i]==3)
            {
                ans1+=ksm(2,cnt2)-1;//这个3对答案的贡献 
                ans1%=mod;
                cnt3++;
            }
        ans+=ans1;
        for(int i=pos+1;i<=n;i++)
            if(a[i]==2)//遇到2 
                ans1-=cnt3,ans%=mod,ans+=mod,ans%=mod,ans1*=ksm(2,mod-2),ans1%=mod;
            else if(a[i]==1)
                ans+=ans1,ans%=mod;
            else
                cnt3--;//遇到3 
        wr(ans),puts("");
    }
    return 0;
}