bzoj4818【SDOI2017】序列计数 矩阵快速幂+动态规划

168 篇文章 0 订阅
4 篇文章 0 订阅

讲道理,最后一道题目不应该是防AK题么,为啥这么水。。
首先一眼容斥,用总方案-不含有质数的方案。
设f[i][j]表示前i个数的和对p的取模为j,明显有f[i][j]=sigma(f[i-1][(j-k)%p]);
然后建构矩阵直接快速幂即可。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int M=3e7+5;
const int N=2e6+5;
const int mo=20170408;
int n,m,p;
int pr[N],tot,f[205];
bool vis[M];
struct node
{
    int mat[105][105];
}ans,a;
inline void mul(node &c,node a,node b)
{
    memset(c.mat,0,sizeof(c.mat));
    fo(i,0,p-1)
    fo(j,0,p-1)
    fo(k,0,p-1)
    c.mat[i][j]=(c.mat[i][j]+1ll*a.mat[i][k]*b.mat[k][j]%mo)%mo;
}

inline node pow(node a,int b)
{
    memset(ans.mat,0,sizeof(ans.mat));
    fo(i,0,p-1) ans.mat[i][i]=1;
    while (b)
    {
        if (b&1) mul(ans,ans,a);
        mul(a,a,a);
        b>>=1;
    }
    return ans;
}

inline int solve1()
{
    fo(i,1,m) f[i%p]++;
    fo(j,1,m) a.mat[(-j%p+p)%p][0]++;
    fo(i,1,p-1)
    fo(j,0,p-1)
            a.mat[j][i]=a.mat[(j-1+p)%p][i-1];
    a=pow(a,n-1);
    int ans=0;
    fo(i,0,p-1) 
    ans=(ans+1ll*f[i]*a.mat[i][0]%mo)%mo;
    return ans;
}

inline int solve2()
{
    memset(f,0,sizeof(f));
    fo(i,1,m)
    if (vis[i]) f[i%p]++;
    memset(a.mat,0,sizeof(a.mat));
    fo(j,1,m)
    if (vis[j]) a.mat[(-j%p+p)%p][0]++;
    fo(i,1,p-1)
    fo(j,0,p-1)
    a.mat[j][i]=a.mat[(j-1+p)%p][i-1];
    a=pow(a,n-1);
    int ans=0;
    fo(i,0,p-1)  
    ans=(ans+1ll*f[i]*a.mat[i][0]%mo)%mo;
    return ans;
}

int main()
{
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    scanf("%d%d%d",&n,&m,&p);
    vis[1]=1;
    fo(i,2,m)
    {
        if (!vis[i]) pr[++tot]=i;
        fo(j,1,tot)
        {
            if (i*pr[j]>m)break;
            vis[i*pr[j]]=1;
            if (i%pr[j]==0) break;
        }
    }
    int ans1=solve1();
    int ans2=solve2();
    printf("%d",(ans1-ans2+mo)%mo);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值