CF509E Pretty Song 题解

看到这题要求每个子串对答案的贡献,于是想到了 SAM。写了一个 SAM 后才发现复杂度有大问题(

子串不好处理,我们可以想想字母 I,E,A,O,U,Y 对每个子串的贡献。

如图,我们假设现在在第 \(i\) 个字母,它是一个特殊字母(例如 I)。包含它的子串就是在 \([1,i]\)\([i,n]\) 各选取一个端点组成的子串。

我们先考虑模拟这个过程。

枚举每个左端点,看左右端点组成的子串的长度范围。

\([1,r],[2,r-1],[3,r-2]...,[l-1,l+r-2],[l,l+r-1]\)

我们可以发现可以通过统计对答案有贡献的子串长度个数,最后答案就是 \(\sum_{i=1}^{n}c[i]/i\)

但是这个复杂度是 \(O(n^2)\) 的,依然无法通过此题。

考虑观察差分过程。

可以发现,+1 是连续的一段,-1 也是连续的一段,于是我们可以再来一次差分。

最后这个字母对二次差分数组的影响就是:

c[1]++;c[l+1]--;c[r+1]--;c[l+r]++;

统计完每个字母对答案的贡献最后还原原来的统计数组就行了。

代码:

#include<bits/stdc++.h>
#define pc(x) putchar(x)
#define int long long
#define dl double
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){f=ch=='-'?-1:f;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
void write(int x)
{
    if(x<0){x=-x;putchar('-');}
    if(x>9)write(x/10);
    putchar(x%10+48);
}
int n,c[500005];dl ans;
char s[500005];
int check(int x)
{
    if(x=='I'||x=='E'||x=='A'||x=='O'||x=='U'||x=='Y')return 1;
    return 0;
}
signed main()
{
    scanf("%s",s+1);n=strlen(s+1);
    for(int i=1;i<=n;++i)
    {
        if(!check(s[i]))continue;
        int l=i,r=n-i+1;
        c[1]++;c[l+1]--;
        c[r+1]--;c[l+r]++;
    }
    for(int i=1;i<=n;++i)c[i]+=c[i-1];
    for(int i=1;i<=n;++i)c[i]+=c[i-1];
    for(int i=1;i<=n;++i)ans+=(dl)c[i]/i;
    printf("%.8lf",ans);
    return 0;
}
posted @ 2022-03-24 11:47  violetctl39  阅读(47)  评论(0编辑  收藏  举报