定义 f ( s ) f(s) f(s)表示 s s s最小表示的开头元素的下标,下标从 1 1 1开始,如果有多个满足的元素则取下标最小的那个。例如 f ( a a a a ) = 1 , f ( b a b c ) = 2 , f ( q w e q w e q w e ) = 3 f(aaaa)=1,f(babc)=2,f(qweqweqwe)=3 f(aaaa)=1,f(babc)=2,f(qweqweqwe)=3,因为三个字符串的最小表示分别为 a a a a , a b c b , e q w e q w e q w aaaa,abcb,eqweqweqw aaaa,abcb,eqweqweqw。
纱雾有 n n n个字符串 s i s_i si,第 i i i个字符串的长度为 a i a_i ai。
纱雾的字符串是有魔力的,只要使用一个咒语,每个 s i s_i si会等概率地随机变成所有长度为 a i a_i ai且只包含小写字母的 2 6 a i 26^{a_i} 26ai个字符串的其中一个。
请你求出 ∑ i = 1 n [ f ( s i ) = f ( s ( i m o d n ) + 1 ) ] \sum\limits_{i=1}^n[f(s_i)=f(s_{(i\bmod n)+1})] i=1∑n[f(si)=f(s(imodn)+1)]的期望值,输出答案模 998244353 998244353 998244353后的值。
1 ≤ n , a i ≤ 1 0 5 1\leq n,a_i\leq 10^5 1≤n,ai≤105
有一个子任务满足 a i a_i ai全为质数,分值为 30 30 30分。
我们可以对于每个 i i i求 [ f ( s i ) = f ( s ( i m o d n ) + 1 ) ] [f(s_i)=f(s_{(i\bmod n)+1})] [f(si)=f(s(imodn)+1)]的期望值。
一个字符串的 s s s的 f f f值和它的循环节长度有关,设循环节的长度为 d d d,则 f ( s ) ∈ [ 1 , d ] f(s)\in [1,d] f(s)∈[1,d],且 f ( s ) = i ∈ [ 1 , d ] f(s)=i\in [1,d] f(s)=i∈[1,d]的概率是相等的因为每组循环同构的字符串在每个位置都有 1 1 1的贡献。
当 a i a_i ai为质数时,循环节长度只会有 1 1 1和 a i a_i ai,概率分别为 26 2 6 a i \dfrac{26}{26^{a_i}} 26ai26和 1 − 26 2 6 a i 1-\dfrac{26}{26^{a_i}} 1−26ai26。
考虑长度分别为 x , y x,y x,y的两个字符串如何计算概率。
f ( s x ) = 1 f(s_x)=1 f(sx)=1的概率是 26 2 6 x + ( 1 − 26 2 6 x ) × 1 x \dfrac{26}{26^x}+(1-\dfrac{26}{26^x})\times \dfrac 1x 26x26+(1−26x26)×x1, f ( s x ) = i ∈ [ 2 , x ] f(s_x)=i\in [2,x] f(sx)=i∈[2,x]的概率都是是 ( 1 − 26 2 6 x ) × 1 x (1-\dfrac{26}{26^x})\times \dfrac 1x (1−26x26)×x1。 f ( s y ) f(s_y) f(sy)同理。
对 f ( s x ) = f ( s y ) = 1 f(s_x)=f(s_y)=1 f(sx)=f(sy)=1和 f ( s x ) = f ( s y ) > 1 f(s_x)=f(s_y)>1 f(sx)=f(sy)>1两种情况分类讨论即可,这样可以拿到 30 30 30分。
那 a i a_i ai不为质数怎么办呢?
设 c n t d cnt_d cntd表示长度为 d d d且循环节长度也为 d d d的字符串个数,用容斥可得
c n t d = 2 6 d − ∑ c ∣ d , c < d c n t c cnt_d=26^d-\sum\limits_{c|d,c
设 v v v为 a i a_i ai的最大值,则我们只需要预处理 d ∈ [ 1 , v ] d\in [1,v] d∈[1,v]的 c n t d cnt_d cntd。 [ 1 , v ] [1,v] [1,v]上每个数的约数和的和为 v + v 2 + v 3 + ⋯ + 1 = v ln v v+\dfrac v2+\dfrac v3+\cdots+1=v\ln v v+2v+3v+⋯+1=vlnv,那么我们可以 O ( v ln v ) O(v\ln v) O(vlnv)预处理出所有 c n t d cnt_d cntd。
我们枚举 w w w,考虑 f ( s x ) = f ( s y ) = w f(s_x)=f(s_y)=w f(sx)=f(sy)=w的概率。枚举两个字符串的循环节 d x d_x dx和 d y d_y dy,则概率为
( ∑ d x ∣ x , d x ≥ w c n t d x 2 6 x × 1 d x ) × ( ∑ d y ∣ y , d y ≥ w c n t d y 2 6 y × 1 d y ) (\sum\limits_{d_x|x,d_x\geq w}\dfrac{cnt_{d_x}}{26^x}\times \dfrac{1}{d_x})\times (\sum\limits_{d_y|y,d_y\geq w}\dfrac{cnt_{d_y}}{26^y}\times \dfrac{1}{d_y}) (dx∣x,dx≥w∑26xcntdx×dx1)×(dy∣y,dy≥w∑26ycntdy×dy1)。
其实我们可以只枚举 d x d_x dx和 d y d_y dy,那么此时符合条件的 f f f的个数是 min ( d x , d y ) \min(d_x,d_y) min(dx,dy)。
这样的话,每次需要枚举 x x x和 y y y的约数, x x x和 y y y的约数个数都是 O ( v ) O(\sqrt v) O(v),那总共要枚举 O ( v ) O(v) O(v)次。如果我们用双指针,就可以只枚举 O ( v ) O(\sqrt v) O(v)次。求逆元的时候要用到快速幂,所以时间复杂度为 O ( n v log p ) O(n\sqrt v\log p) O(nvlogp),其中 p p p为模数,即 998244353 998244353 998244353。我们继续优化。
对于每个 x x x的每个约数 d x d_x dx,我们都可以 O ( log n ) O(\log n) O(logn)预处理出 c n t d x 2 6 x × 1 d x \dfrac{cnt_{d_x}}{26^x}\times \dfrac{1}{d_x} 26xcntdx×dx1,那么预处理的总时间复杂度为 O ( n ln v log p ) O(n\ln v\log p) O(nlnvlogp),其中 p p p为模数,即 998244353 998244353 998244353。那么,上面就不用再用 O ( log p ) O(\log p) O(logp)的时间复杂度来求逆元了,计算答案的部分的时间复杂度就降为 O ( n v ) O(n\sqrt v) O(nv)。
时间复杂度为 O ( n ln v log p + n v ) O(n\ln v\log p+n\sqrt v) O(nlnvlogp+nv)。
#include
using namespace std;
const int N=100000;
const long long mod=998244353;
int n,a[N+5];
long long ans=0,cnt[N+5];
vector<int>v[N+5];
vector<long long>s[N+5];
long long mi(long long t,long long v){
if(!v) return 1;
long long re=mi(t,v/2);
re=re*re%mod;
if(v&1) re=re*t%mod;
return re;
}
int gt(int x,int dx){
return cnt[dx]*mi(mi(26,x),mod-2)%mod*mi(dx,mod-2)%mod;
}
void init(){
for(int i=N;i>=1;i--){
for(int j=i;j<=N;j+=i){
v[j].push_back(i);
}
}
long long tmp=1;
for(int i=1;i<=N;i++){
tmp=tmp*26%mod;
cnt[i]=tmp;
for(int j:v[i]){
if(i==j) continue;
cnt[i]=(cnt[i]-cnt[j]+mod)%mod;
}
}
for(int i=1;i<=N;i++){
for(int j:v[i]){
s[i].push_back(gt(i,j));
}
}
}
void solve(int v1,int v2){
// for(int i:v[v1]){
// for(int j:v[v2]){
// ans=(ans+gt(i,v1)*gt(j,v2)%mod*min(i,j)%mod)%mod;
// }
// }
int now=0;
long long s1=0,s2=0;
for(int j=0;j<v[v2].size();j++) s2=(s2+s[v2][j]*v[v2][j]%mod)%mod;
for(int i=0;i<v[v1].size();i++){
while(now<v[v2].size()&&v[v2][now]>=v[v1][i]){
s1=(s1+s[v2][now])%mod;
s2=(s2-s[v2][now]*v[v2][now]%mod+mod)%mod;
++now;
}
ans=(ans+s[v1][i]*s1%mod*v[v1][i]%mod+s[v1][i]*s2%mod)%mod;
}
}
int main()
{
// freopen("sagiri.in","r",stdin);
// freopen("sagiri.out","w",stdout);
init();
scanf("%*d%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
if(n==1){
printf("1");
return 0;
}
for(int i=1;i<=n;i++){
solve(a[i],a[i%n+1]);
}
printf("%lld",ans);
return 0;
}