题目
做法
这都没想到
首先,我们需要考虑有质数的,和没质数的情况。
但是我们发现,有质数的情况不可能用来推导没质数的情况,且总情况减去没有质数的情况就是有质数的情况,且有质数的情况需要用没质数的情况推导而成。
所以直接用处理无质数的情况和总情况(事实上,总情况总比无质数情况好处理,而无质数情况比有质数情况好处理),然后容斥求得即可,以此减少思考难度以及思考总量(事实上,用容斥一般可以减少常数甚至是复杂度,本题减少的是常数)。
现在说总情况的统计方法:
考虑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...cntp−1cnt0cnt1...cntp−2cntp−1cnt0...............⎦⎥⎥⎤
把 f [ i ] f[i] f[i]看成 1 ∗ p 1*p 1∗p的矩阵,那么 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;
}
小结
- 在思考的过程中,如果思考的两个部分(或者多个部分)之间,存在容斥,可以通过思考容斥减少情况,以此减少思考难度。(当然,思维要放开,有时候不一定要用容斥,但是在本题容斥一般比不容斥优秀)。
- 有时候一次性想不到正解的话,可以通过考虑时间较大的解法并进行优化,因为有的题目可能就是优化题。