BZOJ4818: [Sdoi2017]序列计数

Posted QYP_2002

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ4818: [Sdoi2017]序列计数相关的知识,希望对你有一定的参考价值。

4818: [Sdoi2017]序列计数

Time Limit: 30 Sec  Memory Limit: 128 MB
Submit: 754  Solved: 461
[Submit][Status][Discuss]

Description

Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数。Alice还希望
,这n个数中,至少有一个数是质数。Alice想知道,有多少个序列满足她的要求。
 

Input

一行三个数,n,m,p。
1<=n<=10^9,1<=m<=2×10^7,1<=p<=100
 

Output

一行一个数,满足Alice的要求的序列数量,答案对20170408取模。
 

Sample Input

3 5 3

Sample Output

33

 思路{

  水题一道....首先容斥一下,$Ans=$所有的方案数-不含质数的方案数.

  二维DP转移是很好想的 $ F [ i ] [ j ] $ 表示前$ i $个数和 $ %p $为$ j $的方案数.不含质数的同理

   $ F [ i ] [ (j+x) ] + = F [ i - 1] [ j]  $

  这个暴力DP不对,看到n那么大,不含$Max,Min$等操作,直接矩阵快速幂了.

  构造转移矩阵时可以第一行枚举m,求出来.

  然后发现第二行以后都是上一行往左移动一位.

  那么不用$ O( m*p) $的构造,直接$ O(m*m) $了.

  在BZOJ上跑得贼慢......

}

#include<bits/stdc++.h>
#define il inline
#define RG register
#define ll long long
#define db double
#define N 110
#define mod 20170408
using namespace std;
bool vis[20000000];int P[2000000];
void pre(){
  vis[1]=true;
  for(int i=2;i<=20000000;++i){
    if(!vis[i])P[++P[0]]=i;
    for(int j=1;j<=P[0]&&P[j]*i<=20000000;++j){
      vis[i*P[j]]=true;
      if(!(i%P[j]))break;
    }
  }
}
int ss[N];
struct matrix{
  int n,m;
  ll ma[N][N];
  matrix operator *(const matrix & a)const{
    matrix c;memset(c.ma,0,sizeof(c.ma));
    if(m!=a.n)return c;c.n=n,c.m=a.m;
    for(RG int i=1;i<=c.n;++i)
      for(RG int j=1;j<=c.m;++j){
	for(RG int h=1;h<=a.n;++h){
	    c.ma[i][j]+=(ma[i][h]*a.ma[h][j])%mod;
	    if(c.ma[i][j]>=mod)c.ma[i][j]-=mod;
	  }
      }
    return c;
  }
}ans,temp;
matrix qp(matrix a,ll b){
  matrix Ans;Ans.n=Ans.m=a.n;
  for(int i=1;i<=a.n;++i)
    for(int j=1;j<=a.n;++j)
      Ans.ma[i][j]=(i==j);
  for(;b;b>>=1,a=a*a)
    if(b&1)Ans=Ans*a;
  return Ans;
}
int n,m,p;
int main(){
  scanf("%d%d%d",&n,&m,&p);pre();
  ans.n=p,ans.m=1;
  for(int i=1;i<=m;++i){
    ans.ma[(i%p)+1][1]++;
    if(ans.ma[(i%p)+1][1]>=mod)ans.ma[(i%p)+1][1]-=mod;
  }
  for(int i=1;i<=m;++i){
    int tmp=i%p;tmp=p-tmp;tmp++;
    if(tmp>p)tmp=1;
    temp.ma[1][tmp]++;
    if(temp.ma[1][tmp]>=mod)temp.ma[1][tmp]-=mod;
  }
  for(int i=2;i<=p;++i){
    for(int j=2;j<=p;++j)
      temp.ma[i][j]=temp.ma[i-1][j-1];
    temp.ma[i][1]=temp.ma[i-1][p];
  }temp.n=temp.m=p;
  temp=qp(temp,n-1);
  ans=temp*ans;
  ll Ans1=ans.ma[1][1];
  memset(ans.ma,0,sizeof(ans.ma));
  for(int i=1;i<=m;++i)
    if(vis[i]){
      ans.ma[(i%p)+1][1]++;
      if(ans.ma[(i%p)+1][1]>=mod)ans.ma[(i%p)+1][1]-=mod;
    }
  memset(temp.ma,0,sizeof(temp.ma));
  for(int i=1;i<=m;++i){
    if(!vis[i])continue;
    int tmp=i%p;tmp=p-tmp;tmp++;
    if(tmp>p)tmp=1;
    temp.ma[1][tmp]++;
    if(temp.ma[1][tmp]>=mod)temp.ma[1][tmp]-=mod;
  }
  for(int i=2;i<=p;++i){
    for(int j=2;j<=p;++j)
      temp.ma[i][j]=temp.ma[i-1][j-1];
    temp.ma[i][1]=temp.ma[i-1][p];
  }temp.n=temp.m=p;
  ans.n=p,ans.m=1;
  temp=qp(temp,n-1);
  ans=temp*ans;
  ll Ans2=ans.ma[1][1];
  cout<<(Ans1-Ans2+mod)%mod;
  return 0;
}

  

以上是关于BZOJ4818: [Sdoi2017]序列计数的主要内容,如果未能解决你的问题,请参考以下文章

bzoj4818 Sdoi2017—序列计数

BZOJ_4818_[Sdoi2017]序列计数_矩阵乘法

[bzoj4818][Sdoi2017]序列计数

[bzoj4818][Sdoi2017]序列计数_矩阵乘法_欧拉筛

bzoj 4818: [Sdoi2017]序列计数

BZOJ 4818 SDOI2017 序列计数