题目
来骗,来偷袭
分析
这道题让我们求
∑
i
=
1
∣
S
∣
f
i
(
S
)
∗
(
∣
S
∣
+
1
)
i
−
1
m
o
d
998244353
\sum_{i=1}^{|S|}f_i(S)*(|S|+1)^{i-1}mod 998244353
i=1∑∣S∣fi(S)∗(∣S∣+1)i−1mod998244353
的值。
然后一看数据范围,
n
≤
1
0
6
n\le 10^6
n≤106。于是我们就要在平均最多
O
(
l
o
g
O(log
O(log
n
)
n)
n)的时间里把这个
f
i
(
S
)
f_i(S)
fi(S)给算完。
那么,这个函数该怎么算呢。
我们可以发现,对于满分为
i
i
i的一场比赛,最多会有
n
i
\frac{n}{i}
in个对局。那么,我们就要在
O
(
1
)
O(1)
O(1)的时间里算完每一场对局。
于是我们可以开几个数组,记录
W
,
L
W,L
W,L的个数和每个
W
,
L
W,L
W,L的位置。然后通过类似
K
M
P
KMP
KMP算法的方式,用一个数组
n
x
t
x
nxt_x
nxtx表示第
x
x
x位之后最小的
i
i
i,满足
S
i
=
=
S
i
−
1
S_i==S_{i-1}
Si==Si−1
然后可以发现,如果一局对局要持续,两人必须交替输赢,由此,可以用刚刚的
n
x
t
nxt
nxt数组来得到两场对局的分割点,于是我们就可以在
O
(
l
o
g
O(log
O(log
n
)
n)
n)的时间里,找出每一场对局。
最后计算
a
n
s
ans
ans即可
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll sumW[1005000],sumL[1000500],posW[1000500],posL[1000500];
ll nxt[1000500];
char s[1005000];
ll len;
const ll mod=998244353;
int main()
{
scanf("%lld",&len);
scanf("%s",s+1);
for (ll i=1;i<=len;i++)
{
sumW[i]=sumW[i-1];
sumL[i]=sumL[i-1];
if (s[i]=='W')
{
sumW[i]++;
posW[sumW[i]]=i;
}
else
{
sumL[i]++;
posL[sumL[i]]=i;
}
}
nxt[len]=nxt[len+1]=len+1;
for (ll i=len-1;i>=1;i--)
if (s[i]==s[i+1]) nxt[i]=i+1;
else nxt[i]=nxt[i+2];
ll ans=0,p=1;
for (ll i=1;i<=len;i++)
{
ll cnt=0,j=0;
while(j<=len)
{
ll nj=len+1;
if (sumL[j]+i<=sumL[len])
nj=min(nj,posL[sumL[j]+i]);
if (sumW[j]+i<=sumW[len])
nj=min(nj,posW[sumW[j]+i]);
if (nj>len)break;
ll l=sumL[nj]-sumL[j],w=sumW[nj]-sumW[j];
if (abs(l-w)==1) nj=nxt[nj];
if (nj>len)break;
l=sumL[nj]-sumL[j];
w=sumW[nj]-sumW[j];
if (l<w) cnt++;
j=nj;
}
ans=(ans+cnt*p)%mod;
p=p*(len+1)%mod;
}
printf("%lld\n",ans);
}