[HDU 7055]Yiwen with Sqc 题解

参考 HDU - 7055 Yiwen with Sqc (2021“MINIEYE杯”中国大学生算法设计超级联赛(7)1012)(推公式,差分)_weifeng2356的博客-CSDN博客

Statement

给定只包含小写字母且长度为 \(n\) 字符串 \(s\),定义 \(sqc(s,l,r,c)\) 表示在子串 \(s_l…s_r\) 中,字符 \(c\) 出现的次数。

\(\sum_{c=97}^{122}\sum_{i=1}^n\sum_{j=1}^n sqc(s,i,j,c)^2\bmod 998244353\)

其中 \(97、122\) 分别为字符az\(ASCII\) 码。

\(1≤T≤100,|s|≤1e5,∑|s|≤5e6\)

Solution

可以想到每个字符无关,可以分别处理

考虑处理到字符 \(a\) ,我们把字符串中为 \(a\) 的部分标为 \(1\),其他为 \(0\)

原字符串被我们分成了几个以 \(1\) 为分割点的段(红色段)

\(a\) 一共出现 \(cnt\) 次,出现的第 \(i\) 个位置为 \(a_i\)

特别的 \(a_0=0,a_{cnt+1}=n+1\)

那么每一红色段长度为 \(len_i=a_{i+1}-a_i\)

考虑固定右端点 \(r\) 在第 \(i\) 个区间,那么贡献为 \(ans[i]=\sum_{j=0}^{i-1}len[j]\times(i-j)^2\)

\(r\) 可以在第 \(i\) 个区间内任意移动,设区间 \(i\) 的贡献为 \(len[i]\times ans[i]\)

考虑如何在 \(O(n)\) 的时间求出 \(i=1..cnt\)\(ans[i]\)

平方项不好处理,我们可以通过差分的方式干掉最高次项

我们对 \(ans\) 做两次差分

\[\begin{align*} dif_i&=ans_i-ans_{i-1}\\ &=\sum_{j=0}^{i-2}len[j]\times(i-j)^2-\sum_{j=0}^{i-2}len[j]\times(i-j-1)^2+len[i-1]\\ &=len[i-1]-\sum_{j=0}^{i-2}len[j]\times(2j-2i+1) \end{align*} \]

\(diff=dif_i-dif_{i-1}\)

\[\begin{align*} diff_i&=dif_i-dif_{i-1}\\ &=len[i-1]-\sum_{j=0}^{i-2}len[j]\times(2j-2i+1)-len[i-2]+\sum_{j=0}^{i-3}len[j]\times(2j-2i+3)\\ &=2\sum_{j=0}^{i-3}len[j]-len[i-2]\times(2i-4-2i+1)+len[i-1]-len[i-2]\\ &=2\sum_{j=0}^{i-2}len[j]+len[i-1]\\ &=2\times a[i-1]+a[i]-a[i-1]\\ &=a[i-1]+a[i] \end{align*} \]

容易维护 \(diff\) ,容易维护 \(dif\) ,容易维护 \(ans\) ,容易维护 \(ans[i]\times len[i]\) ,容易得到答案

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+5;
const int mod = 998244353;

int read(){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    return s*w;
}

int T,n,ans,f[N],g[N];
vector<int>a[30];
char s[N];

int calc(int x){
    int res=0,ans=0,dif=0;
    for(int i=1;i<a[x].size()-1;++i)
        (dif+=a[x][i]+a[x][i-1])%=mod,(ans+=dif)%=mod,
        (res+=(a[x][i+1]-a[x][i])*ans)%=mod;
    return res;
}

signed main(){
    T=read();
    while(T--){
        scanf("%s",s+1),n=strlen(s+1),ans=0;
        for(int i=0;i<=25;++i)a[i].clear(),a[i].push_back(0);
        for(int i=1;i<=n;++i)a[s[i]-'a'].push_back(i);
        for(int i=0;i<=25;++i)a[i].push_back(n+1);
        for(int i=0;i<=25;++i)(ans+=calc(i))%=mod;
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2021-10-15 21:05  _Famiglistimo  阅读(65)  评论(0编辑  收藏  举报