[SDOI2017]序列计数(矩阵乘法)

本文详细介绍了如何利用数学和编程技巧,特别是矩阵快速幂算法,来解决关于质数和无质数计数的问题。通过容斥原理简化思考,并给出了一种优化的解决方案,降低时间复杂度到O(p^3 log n)。同时,文章探讨了在无法立即找到最优解时,如何从复杂解法入手逐步优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

题目

做法

这都没想到

首先,我们需要考虑有质数的,和没质数的情况。

但是我们发现,有质数的情况不可能用来推导没质数的情况,且总情况减去没有质数的情况就是有质数的情况,且有质数的情况需要用没质数的情况推导而成。

所以直接用处理无质数的情况和总情况(事实上,总情况总比无质数情况好处理,而无质数情况比有质数情况好处理),然后容斥求得即可,以此减少思考难度以及思考总量(事实上,用容斥一般可以减少常数甚至是复杂度,本题减少的是常数)。

现在说总情况的统计方法:

考虑DP, f [ i ] [ j ] f[i][j] f[i][j],表示第 i i i个位置和模 p p p j j j的情况个数,然后通过转移方程便可以得到,设 c n t i cnt_{i} cnti为不超过 m m m的且模 p p p等于 i i i的正整数个数,即: f [ i + 1 ] [ ( k + j ) % p ] + = f [ i ] [ j ] ∗ c n t k f[i+1][(k+j)\%p]+=f[i][j]*cnt_k f[i+1][(k+j)%p]+=f[i][j]cntk,不难发现,这个转移可以写成矩阵的形式:
[ c n t 0 c n t p − 1 c n t p − 2 . . . c n t 1 c n t 0 c n t p − 1 . . . c n t 2 c n t 1 c n t 0 . . . . . . . . . . . . . . . ] \begin{bmatrix} cnt_0 &cnt_{p-1} & cnt_{p-2} & ...\\ cnt_1 & cnt_0 & cnt_{p-1} & ...\\ cnt_2 & cnt_1 & cnt_0 & ...\\ ... & ... & ... & ... \end{bmatrix} cnt0cnt1cnt2...cntp1cnt0cnt1...cntp2cntp1cnt0...............

f [ i ] f[i] f[i]看成 1 ∗ p 1*p 1p的矩阵,那么 f [ i ] f[i] f[i]转移到 f [ i + 1 ] f[i+1] f[i+1]就是乘这个矩阵。

直接矩阵快速幂加速即可。

至于没有质数的情况,一个很简单的方法就是把 c n t i cnt_{i} cnti的定义加上不是质数。

如果你是在不想用容斥,也可以直接把DP设成 f [ i ] [ j ] [ 0 / 1 ] f[i][j][0/1] f[i][j][0/1],分别表示有质数和没有质数,然后化成二维的 D P DP DP f [ i ] [ j / j + p ] f[i][j/j+p] f[i][j/j+p],但是事实上非常的没有必要,因为矩阵长度乘二,相当于常数乘 8 8 8,大概率过不了。

代码

时间复杂度: O ( p 3 log ⁡ n ) O(p^3\log{n}) O(p3logn)

#include<cstdio>
#include<cstring>
#define  N  21000000
#define  SN  3100000
using  namespace  std;
typedef  long  long  LL;
int  li[SN],top;
bool  v[N];
int  n,m,p;
LL  cn1[110],cn2[110];
LL  mod=20170408;
void  XXS()
{
	cn1[1]=cn2[1]=1;
	for(int  i=2;i<=m;i++)
	{
		cn1[i%p]++;
		if(!v[i])li[++top]=i;
		else  cn2[i%p]++;
		for(int  j=1;j<=top  &&  i*li[j]<=m;j++)
		{
			v[li[j]*i]=1;
			if(i%li[j]==0)break;
		}
	}
}
struct  node
{
	LL  a[110][110];
	node(){memset(a,0,sizeof(a));}
};
inline  node  operator*(node  x,node  y)
{
	node  z;
	for(int  i=0;i<p;i++)
	{
		for(int  j=0;j<p;j++)
		{
			for(int  k=0;k<p;k++)z.a[i][j]+=x.a[i][k]*y.a[k][j]%mod,z.a[i][j]%=mod;
		}
	}
	return  z;
}
inline  node  ksm(node  x,int  y)
{
	node  ans;
	for(int  i=0;i<p;i++)ans.a[i][i]=1;
	while(y)
	{
		if(y&1)ans=ans*x;
		x=x*x;y>>=1;
	}
	return  ans;
}
int  main()
{
	scanf("%d%d%d",&n,&m,&p);
	XXS();
	LL  ans=0;node  x;
	for(int  i=0;i<p;i++)
	{
		for(int  j=0;j<p;j++)x.a[i][(j+i)%p]=cn1[j];
	}
	x=ksm(x,n);
	ans=x.a[0][0];
	for(int  i=0;i<p;i++)
	{
		for(int  j=0;j<p;j++)x.a[i][(j+i)%p]=cn2[j];
	}
	x=ksm(x,n);
	ans-=x.a[0][0];
	ans=(ans+mod)%mod;
	printf("%lld\n",ans);
	return  0;
}

小结

  1. 在思考的过程中,如果思考的两个部分(或者多个部分)之间,存在容斥,可以通过思考容斥减少情况,以此减少思考难度。(当然,思维要放开,有时候不一定要用容斥,但是在本题容斥一般比不容斥优秀)。
  2. 有时候一次性想不到正解的话,可以通过考虑时间较大的解法并进行优化,因为有的题目可能就是优化题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值