NOIP2023模拟14联测35 sagiri

题目大意

定义 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=1n[f(si)=f(s(imodn)+1)]的期望值,输出答案模 998244353 998244353 998244353后的值。

1 ≤ n , a i ≤ 1 0 5 1\leq n,a_i\leq 10^5 1n,ai105

有一个子任务满足 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}} 126ai26

考虑长度分别为 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+(126x26)×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 (126x26)×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,ccntd=26dcd,c<dcntc

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}) (dxx,dxw26xcntdx×dx1)×(dyy,dyw26ycntdy×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(nv logp),其中 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 )

code

#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;
}

你可能感兴趣的:(题解,好题,题解,c++)