BZOJ 2326 [HNOI2011]数学作业

题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=2326

题解

分位讨论,记f[i]为Concatenate(1..i)的值,对于k位数

[f[i]i+11]=[f[i1]i1]10k00010011 [ f [ i ] i + 1 1 ] = [ f [ i − 1 ] i 1 ] ⋅ [ 10 k 0 0 0 1 1 0 0 1 ]

分类讨论+矩乘优化dp,细节很难处理。

代码

#include <cstdio>

typedef unsigned long long ull;

int mod;

struct matrix
{
  int n,m,a[3][3];

  matrix(int _n=1,int _m=1)
  {
    n=_n;
    m=_m;
    for(int i=0; i<n; ++i)
      {
        for(int j=0; j<m; ++j)
          {
            a[i][j]=0;
          }
      }
    if(n==m)
      {
        for(int i=0; i<n; ++i)
          {
            a[i][i]=1;
          }
      }
  }

  matrix operator *(const matrix &other) const
  {
    matrix res;
    res.n=n;
    res.m=other.m;
    for(int i=0; i<n; ++i)
      {
        for(int j=0; j<other.m; ++j)
          {
            int ans=0;
            for(int k=0; k<m; ++k)
              {
                ans=(ans+1ll*a[i][k]*other.a[k][j])%mod;
              }
            res.a[i][j]=ans;
          }
      }
    return res;
  }

  int print()
  {
    for(int i=0; i<n; ++i)
      {
        for(int j=0; j<m; ++j)
          {
            printf("%d ",a[i][j]);
          }
        puts("");
      }
    return 0;
  }
};

int quickpow(int a,long long b)
{
  int res=1;
  while(b)
    {
      if(b&1)
        {
          res=1ll*res*a%mod;
        }
      a=1ll*a*a%mod;
      b>>=1;
    }
  return res;
}

matrix quickpow(matrix a,long long b)
{
  matrix res(a.n,a.m);
  while(b)
    {
      if(b&1)
        {
          res=res*a;
        }
      a=a*a;
      b>>=1;
    }
  return res;
}

int ans;
long long n;
matrix st(1,3),trans(3,3);

int main()
{
  scanf("%lld%d",&n,&mod);
  trans.a[1][0]=1;
  trans.a[2][1]=1;
  long long start;
  ull end;
  for(start=1; end=(ull)start*10-1,end<(ull)n; start=end+1)
    {
      st.a[0][0]=start%mod;
      st.a[0][1]=(start+1)%mod;
      st.a[0][2]=1;
      trans.a[0][0]=(end+1)%mod;
      st=st*quickpow(trans,end-start);
      ans=((1ll*ans*quickpow((end+1)%mod,end-start+1))+st.a[0][0])%mod;
    }
  st.a[0][0]=start%mod;
  st.a[0][1]=(start+1)%mod;
  st.a[0][2]=1;
  trans.a[0][0]=(end+1)%mod;
  st=st*quickpow(trans,n-start);
  ans=((1ll*ans*quickpow((end+1)%mod,n-start+1))+st.a[0][0])%mod;
  printf("%d\n",ans);
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值