题目大意
有一个长度为nnn的数列a1,a2,…,ana_1,a_2,\dots,a_na1,a2,…,an,其中1≤ai≤91\leq a_i\leq 91≤ai≤9。对于相邻两项的aia_iai和ai+1a_{i+1}ai+1,满足gcd(ai,ai+1)=1\gcd(a_i,a_{i+1})=1gcd(ai,ai+1)=1时,你可以通过一次操作交换aia_iai和ai+1(1≤i<n)a_{i+1}(1\leq i<n)ai+1(1≤i<n)。
求可以通过这样的操作获得多少个序列(包括原序列)。输出答案模998244353998244353998244353后的值。
2≤n≤105,1≤ai≤92\leq n\leq 10^5,1\leq a_i\leq 92≤n≤105,1≤ai≤9
题解
对于ai=1/5/7a_i=1/5/7ai=1/5/7时,这些数可以放在任意位置(注意交换两个相同的数相当于不交换),我们可以将其放在最后处理。下面只考虑不是1,5,71,5,71,5,7的aia_iai。
然后,我们将剩下的数分成222的倍数和333的倍数:
- 2,4,6,82,4,6,82,4,6,8
- 3,6,93,6,93,6,9
注意任意两个在[1,9][1,9][1,9]中不相同的数的gcd\gcdgcd如果不为111,则一定能被222或333整除。
也就是说,在序列中,上面两类数的相对顺序是不变的。而666和其他数都不能交换,也就是说666的位置已经固定了。
对于两个666之间的数,这些数中222的倍数和333的倍数这两类数没有交集。设有xxx个数是222的倍数,有yyy个数是333的倍数,那么就相当于要在这一段中选择xxx个位置放222的倍数,剩下的位置放333的倍数,方案数为(x+yx)\binom{x+y}{x}(xx+y)。把每一段的(x+yx)\binom{x+y}{x}(xx+y)相乘即可。
下面考虑值为1/5/71/5/71/5/7的aia_iai的贡献。设111的个数为w1w_1w1,555的个数为w2w_2w2,777的个数为w3w_3w3,上面得到的答案为nownownow,则答案为:
ans=(nw1+w2+w3)(w1+w2+w3w1)(w2+w3w2)×nowans=\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 nowans=(w1+w2+w3n)(w1w1+w2+w3)(w2w2+w3)×now
也就是先在nnn个位置中选出w+w2+w3w_+w_2+w_3w+w2+w3个位置放1,5,71,5,71,5,7,并在这些位置中分配1,5,71,5,71,5,7各自的位置,再乘上其他数的分配方案。
时间复杂度为O(n)O(n)O(n)。
code
#include<bits/stdc++.h>
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;
}
392

被折叠的 条评论
为什么被折叠?



