Clarke and digits(矩阵快速幂+dp)

Clarke and digits

Time Limit: 5000/3000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 277 Accepted Submission(s): 176

Problem Description

Clarke is a patient with multiple personality disorder. One day, Clarke turned into a researcher, did a research on digits.
He wants to know the number of positive integers which have a length in and are divisible by and the sum of any adjacent digits can not be .

Input

The first line contains an integer , the number of the test cases.
Each test case contains three integers .

Output

Each test case print a line with a number, the answer modulo .

Sample Input

2 1 2 5 2 3 5

Sample Output

13 125

Hint:

At the first sample there are 13 number:
7,21,28,35,42,49,56,63,70,77,84,91,98 7 , 21 , 28 , 35 , 42 , 49 , 56 , 63 , 70 , 77 , 84 , 91 , 98 satisfied.

Clarke and digits

题意大概就是位数在l-r中能被整除7且任意相邻位数差不为k的数有多少个,答案对1e9+7取模。




解:

这也是一个一眼题(一眼咋做)。我们用 fi,j f i , j 表示末尾为 i i 且模7为j的数有多少。

然后矩阵状态转移。

显然不能手推,用手推的都凉了,而且这个矩阵是不确定的,因为题目有一个输入 k k 。简单讲一下,状态矩阵的一般姿势:先把所有状态排成一排。然后开一个状态数平方的矩阵A。如果状态 p1 p 1 可以转移到状态 p2 p 2 ,假设 p2 p 2 += αp1 α ∗ p 1 。那么我们在 Ap1p2 A ( p 1 , p 2 ) 上加上一个 α α <script type="math/tex" id="MathJax-Element-163">α</script>。然后我们就可以做矩阵转移了。

然后由于这道题不好做初始状态,我们在状态中加一个表示没有放数,可以转移到放每个数。

贴波代码(矩阵快速幂越来越溜了):

#include<iostream>  
#include<cstring>  
#include<cstdio>  
using namespace std;  
int n=71,T,l,r,c;  
long long const mod=1000000007;  
struct lxy{  
    long long m[105][105];  
    lxy operator *(const lxy &q){  
        lxy C;  
        for(int i=1;i<=n;i++)  
          for(int j=1;j<=n;j++)  
            C.m[i][j]=0;  
        for(int i=1;i<=n;i++)  
          for(int k=1;k<=n;k++)  
          {  
            if(m[i][k]==0) continue;  
            for(int j=1;j<=n;j++)  
              C.m[i][j]=(C.m[i][j]+m[i][k]*q.m[k][j])%mod;  
          }  
        return C;  
    }  
}key,A;  

lxy quick(lxy B,int x)  
{  
    lxy C;  
    for(int i=1;i<=n;i++)  
      for(int j=1;j<=n;j++)  
      {  
        if(i==j) C.m[i][j]=1;  
        else C.m[i][j]=0;  
      }  
    while(x!=0)  
    {  
        if((x&1)==1) C=B*C;  
        B=B*B;  
        x=x>>1;  
    }  
    return C;  
}  

long long solv(int x)//A.m[1][a*7+1+b%7]表示末尾为a,模7为b的数的数量   
{  
    if(x==0) return 0;  
    for(int i=1;i<=n;i++)  
      for(int j=1;j<=n;j++)  
        key.m[i][j]=0;  
    for(int i=1;i<=n;i++)  
      A.m[1][i]=0;  
    A.m[1][71]=1;//71这一维表示前面没有放数  
    for(int i=1;i<=9;i++)//没有放过数可以下一位放任意数  
      key.m[71][i*7+1+i%7]++;  
    key.m[71][71]=1;//可以接着不放数  
    for(int i=0;i<=9;i++)  
      for(int j=0;j<=6;j++)  
        for(int k=0;k<=9;k++)//每个状态枚举下一位放什么  
          if(i+k!=c)//判断是否合法  
            key.m[i*7+1+j][k*7+1+(j*3+k)%7]++;  
    key=quick(key,x);  
    A=A*key;  
    long long ret=0;  
    for(int i=0;i<=9;i++)  
      ret=(ret+A.m[1][i*7+1])%mod;  
    return ret;  
}  

int main()  
{  
    scanf("%d",&T);  
    while(T--)  
    {  
        long long ans=0;  
        scanf("%d%d%d",&l,&r,&c);  
        ans=(solv(r)-solv(l-1))%mod;  
        ans=(ans+mod)%mod;  
        printf("%lld\n",ans);  
    }  
}   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值