NOIP2023模拟9联测30 华二

题目大意

有一个长度为 n n n的数列 a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,,an,其中 1 ≤ a i ≤ 9 1\leq a_i\leq 9 1ai9。对于相邻两项的 a i a_i ai a i + 1 a_{i+1} ai+1,满足 gcd ⁡ ( a i , a i + 1 ) = 1 \gcd(a_i,a_{i+1})=1 gcd(ai,ai+1)=1时,你可以通过一次操作交换 a i a_i ai a i + 1 ( 1 ≤ i < n ) a_{i+1}(1\leq iai+1(1i<n)

求可以通过这样的操作获得多少个序列(包括原序列)。输出答案模 998244353 998244353 998244353后的值。

2 ≤ n ≤ 1 0 5 , 1 ≤ a i ≤ 9 2\leq n\leq 10^5,1\leq a_i\leq 9 2n105,1ai9


题解

对于 a i = 1 / 5 / 7 a_i=1/5/7 ai=1/5/7时,这些数可以放在任意位置(注意交换两个相同的数相当于不交换),我们可以将其放在最后处理。下面只考虑不是 1 , 5 , 7 1,5,7 1,5,7 a i a_i ai

然后,我们将剩下的数分成 2 2 2的倍数和 3 3 3的倍数:

  • 2 , 4 , 6 , 8 2,4,6,8 2,4,6,8
  • 3 , 6 , 9 3,6,9 3,6,9

注意任意两个在 [ 1 , 9 ] [1,9] [1,9]中不相同的数的 gcd ⁡ \gcd gcd如果不为 1 1 1,则一定能被 2 2 2 3 3 3整除。

也就是说,在序列中,上面两类数的相对顺序是不变的。而 6 6 6和其他数都不能交换,也就是说 6 6 6的位置已经固定了。

对于两个 6 6 6之间的数,这些数中 2 2 2的倍数和 3 3 3的倍数这两类数没有交集。设有 x x x个数是 2 2 2的倍数,有 y y y个数是 3 3 3的倍数,那么就相当于要在这一段中选择 x x x个位置放 2 2 2的倍数,剩下的位置放 3 3 3的倍数,方案数为 ( x + y x ) \binom{x+y}{x} (xx+y)。把每一段的 ( x + y x ) \binom{x+y}{x} (xx+y)相乘即可。

下面考虑值为 1 / 5 / 7 1/5/7 1/5/7 a i a_i ai的贡献。设 1 1 1的个数为 w 1 w_1 w1 5 5 5的个数为 w 2 w_2 w2 7 7 7的个数为 w 3 w_3 w3,上面得到的答案为 n o w now now,则答案为:

a n s = ( n w 1 + w 2 + w 3 ) ( w 1 + w 2 + w 3 w 1 ) ( w 2 + w 3 w 2 ) × n o w ans=\binom{n}{w_1+w_2+w_3}\binom{w_1+w_2+w_3}{w_1}\binom{w_2+w_3}{w_2}\times now ans=(w1+w2+w3n)(w1w1+w2+w3)(w2w2+w3)×now

也就是先在 n n n个位置中选出 w + w 2 + w 3 w_+w_2+w_3 w+w2+w3个位置放 1 , 5 , 7 1,5,7 1,5,7,并在这些位置中分配 1 , 5 , 7 1,5,7 1,5,7各自的位置,再乘上其他数的分配方案。

时间复杂度为 O ( n ) O(n) O(n)

code

#include
using namespace std;
const int N=100000;
const long long mod=998244353;
int n,t1=0,w1=0,w2=0,w3=0,a[N+5],t[N+5];
long long ans=1,jc[N+5],ny[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;
}
void init(){
	jc[0]=1;
	for(int i=1;i<=N;i++) jc[i]=jc[i-1]*i%mod;
	ny[N]=mi(jc[N],mod-2);
	for(int i=N-1;i>=0;i--) ny[i]=ny[i+1]*(i+1)%mod;
}
long long C(int x,int y){
	return jc[x]*ny[y]%mod*ny[x-y]%mod;
}
long long solve(int l,int r){
	int v2=0,v3=0;
	for(int i=l;i<=r;i++){
		if(a[i]%2==0) ++v2;
		else if(a[i]%3==0) ++v3;
	}
	return C(v2+v3,v2);
}
int main()
{
//	freopen("b.in","r",stdin);
//	freopen("b.out","w",stdout);
	init();
	scanf("%d",&n);
	t[++t1]=0;
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		if(a[i]==1) ++w1;
		else if(a[i]==5) ++w2;
		else if(a[i]==7) ++w3;
		else if(a[i]==6) t[++t1]=i;
	}
	t[++t1]=n+1;
	for(int i=1;i<t1;i++){
		ans=ans*solve(t[i]+1,t[i+1]-1)%mod;
	}
	ans=ans*C(n,w1+w2+w3)%mod;
	ans=ans*C(w1+w2+w3,w1)%mod*C(w2+w3,w2)%mod;
	printf("%lld",ans);
	return 0;
}

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