【HDU5564 BestCoder Round 62 (div1)B】【DP转矩阵快速幂】Clarke and digits 长度在[l,r]范围内7倍数数个数要求相邻两位不为K

#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include<iostream>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;}
template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;}
const int G=71;//矩阵大小
const int Z=1e9+7;//取模数
struct MX
{
	int v[G][G];
	void O(){MS(v,0);}
	void E(){MS(v,0);for(int i=0;i<G;i++)v[i][i]=1;}
	MX operator * (const MX &b) const
	{
		MX c;c.O();
		for(int i=0;i<G;i++)
		{
			for(int j=0;j<G;j++)
			{
				for(int k=0;k<G;k++)
				{
					c.v[i][j]=(c.v[i][j]+(LL)v[i][k]*b.v[k][j])%Z;
				}
			}
		}
		return c;
	}
	MX operator ^ (int p) const
	{
		MX y;y.E();
		MX x;MC(x.v,v);
		while(p)
		{
			if(p&1)y=y*x;
			x=x*x;
			p>>=1;
		}
		return y;
	}
}a,b,c;
int casenum,casei;
int l,r,K;
int main()
{
	a.O();
	for(int i=1;i<10;i++)
	{
		int j=i%7;
		int o=j*10+i;
		a.v[0][o]=1;
	}
	scanf("%d",&casenum);
	for(casei=1;casei<=casenum;casei++)
	{
		scanf("%d%d%d",&l,&r,&K);//--l;--r;
		b.O();
		for(int i=0;i<7;i++)//枚举余数
		{
			for(int j=0;j<10;j++)//枚举尾数
			{
				int o1=i*10+j;
				for(int u=0;u<7;u++)//枚举余数
				{
					for(int v=0;v<10;v++)//枚举尾数
					{
						int o2=u*10+v;
						b.v[o1][o2]=(j+v!=K&&(i*10+v)%7==u);
					}
				}
			}
		}
		for(int j=0;j<10;j++)b.v[j][70]=1;
		b.v[70][70]=1;
		c=a*(b^r);int ansr=c.v[0][70];
		c=a*(b^(l-1));int ansl=c.v[0][70];
		int ans=(ansr+Z-ansl)%Z;
		printf("%d\n",ans);
	}
	return 0;
}
/*
【trick&&吐槽】
不要一开始就思考解决问题,
可以先简化数据规模,从小数据入手,再优化,就能AC看起来比较难的题目啦!

【题意】
对于一个正整数,如果其恰好是7的倍数,而且任意相邻两位的数都不为K,那么我们称这个数为7-K number,
现在问你,长度在[l,r]之间的7-K number的个数是多少,输出其mod (1e9+7)的值。
其中,K∈[0,18],l<=r<=1e9

【类型】
DP转矩阵快速幂

【分析】
首先,[l,r]大的我们不敢DP。但是我们应该先想到DP的做法,再进而考虑优化与转移——
怎么DP呢?要先考虑状态的设置——
用f[i][j][k]表示长度为i的串,尾数是j,目前%7的余数为k的数串的个数。
我们考虑使得这个数串*10,然后再加上一个尾数。这种做法恰好可以避免出现前导0的情况。
初始情况下,有f[1][x][x%7]=1; x∈[1,9]
那么我们枚举下一位的尾数u,u∈[0,9]便有状态转移方程——
f[i+1][u][(k*10+u)%7]+=f[i][j][k],转移的条件是u+j≠K
最后f[l~r][0~9][0]就是答案
然而这个DP的时间复杂度是 长度(i)*10(j)*7(k)*10(u),长度太大,于是时间复杂度爆炸。
我们寻求的解决办法便是——矩阵快速幂。
(尾数,余数)的可能性一共也不过只有70种,我们列出一个70*70的矩阵b[][]
我们把(余数*10+尾数)定义为状态,那么b[i][j]=1则表示由状态i向状态j可以转移
这样我们求b^r和b^(l-1),a*(b^r)-a*(b^(l-1))就是答案啦。    ?!?!   
QwQ   ?!?!  并不对!这个是错误的。
a*(b^r)对应的是长度恰好为r,b^(l-1)对应的是长度恰好为l-1。
正确的做法是怎样呢?
我们需要累加一个前缀和,于是引入第71维。
使得b[0~9][70]都为1,b[70][70]也为1.
这样a*(b^p),得到的是这个DP转移[0,p-1]次(即长度在[1,p]范围下)的合法数串的个数
于是这样,a*(b^r)-a*(b^(l-1))就是答案啦。

【时间复杂度&&优化】
O(T * 71^3 * (log(r)+log(l)) ),运算上限大概是7e7,可以在1s内AC~

*/


你可能感兴趣的:(矩阵快速幂,题库-HDU)