P1025 万象启星环 官方题解
Gioush OJ · P1025 万象启星环
万象启星环
【题目简述】
求长度为 的有序序列 的数量,满足 , 是 的倍数,且至少有一个 为质数。答案对 取模。
【数据点 1∼41\sim41∼4】
【直接 DP】
直接统计“至少一个质数”不方便,因此改为“所有序列数”减去“不含质数的序列数”。令 表示前 个数已确定、和模 为 的方案数,最后一维区分可选全集与只选非质数。
【转移】
对每个 枚举加入的数,转移到 。复杂度为 。
【数据点 5∼85\sim85∼8】
【重复转移】
滚动数组后,每一层都执行同一个线性变换。令列向量 记录各余数的方案数,若 表示一次转移,则 。
【矩阵】
令
转移矩阵第 项为 。分别对全集与非质数集合快速幂,答案是两者余数 的方案数之差。
【正解】
【计算计数】
可以用带余除法直接求出;再用线性筛找出 中所有质数,从对应余数类扣除即可得到 。
【解法】
构造两张 循环矩阵,分别快速幂 次。初始向量只有余数 的分量为 。两次转移后取余数 分量相减。
【复杂度分析】
线性筛为 ,矩阵快速幂为 ,总复杂度为 。
【参考代码】
#include <cstdio>#include <cstring>#include <algorithm>#include <iostream>using namespace std;const int Mod=998244353;inline int Add(int a,int b){return a+b>=Mod?a+b-Mod:a+b;}inline int Mul(int a,int b){return 1ll*a*b%Mod;}inline int Del(int a,int b){return a-b<0?a-b+Mod:a-b;}int T,n,m,p;int Prime[2000100],cnt=0,Count[2][110];bool Tag[20000010];void Tackle() { Count[0][1%p]++;Count[1][1%p]++; for (int i=2;i<=m;i++) { Count[0][i%p]++; if (!Tag[i]) { Prime[++cnt]=i; } else Count[1][i%p]++; for (int j=1;j<=cnt;j++) { if (1ll*i*Prime[j]>m) {break;} Tag[i*Prime[j]]=true; if (i%Prime[j]==0) {break;} } } return;}struct Matrix { int Num[110][110]; void Init() { memset(Num,0,sizeof(Num)); }}I,Ans1,f,g,Ans2;Matrix operator*(const Matrix &A,const Matrix &B) { Matrix C; for(int i=1;i<=p;i++) { for(int j=1;j<=p;j++) { C.Num[i][j]=0; for(int k=1;k<=p;k++) { C.Num[i][j]=Add(C.Num[i][j],Mul(A.Num[i][k],B.Num[k][j])); } } } return C;}void Print(Matrix A) { for(int i=1;i<=p;i++) { for(int j=1;j<=p;j++) { cout<<A.Num[i][j]<<" "; } puts(""); } return;}Matrix operator^(Matrix A,int b) { Matrix C; C=I; while(b) { if (b&1) C=C*A; A=A*A; b>>=1; } return C;}Matrix operator-(const Matrix &A,const Matrix &B) { Matrix C; C.Init(); for(int i=1;i<=p;i++) { for(int j=1;j<=p;j++) { C.Num[i][j]=Del(A.Num[i][j],B.Num[i][j]); } } return C;}void Solve() { scanf("%d%d%d",&n,&m,&p); cnt=0; memset(Count,0,sizeof(Count)); memset(Tag,0,(m+1)*sizeof(Tag[0])); I.Init(); Ans1.Init();Ans2.Init(); for (int i=1;i<=p;i++) I.Num[i][i]=1; Tackle(); f.Init();g.Init(); Ans1.Num[p][1]=1;Ans2.Num[p][1]=1; for (int i=1;i<=p;i++) { for (int j=1;j<=p;j++) { f.Num[i][j]=Count[0][(j-i+p)%p]; } } for (int i=1;i<=p;i++) { for (int j=1;j<=p;j++) { g.Num[i][j]=Count[1][(j-i+p)%p]; } } Matrix Ans=(f^n)*Ans1-(g^n)*Ans2; printf("%d\n",Ans.Num[p][1]);}int main() { scanf("%d",&T); while(T--) Solve(); return 0;}