本题特别鸣谢菊苣:Gene_INNOCENT
题目链接:https://codeforces.com/gym/101161
题目大意:
题意:
01串,不包含两个连续的1。要求给出满足下述三个条件的01串的个数。(答案模1e9+7)
- 01串长度在 [L,R] 之间 (
)
- 01串长度是K的倍数(
)
- 不包含连续两个1的01串
注:题意转自菊苣博客:https://blog.csdn.net/qq_41552508/article/details/97161365
题目思路:
首先一个显而易见(并不)的DP,设表示长度为i的,以0为结尾的01串数量,
表示长度为i的,以1为结尾的01串数量,以0结尾的话,很显然前面可以跟0和1,但如果以1结尾,那么由于不能出现连续1,所以只能通过结尾为0的转换而来,所以得到DP方程如下:
然后打个表就可以发现,居然是一个斐波那契数列!
当然,也可以通过证明得到:
把这个结论用d数组表示,那么
到现在不要高兴得太早,最难得地方还没有开始!准确的说这才是最简单的部分!
我们题目要求的根本不是这玩意儿!
我们要得到的是d[k]+d[2k]+d[3k]+....
给这个前缀和定义一个数组叫sum[i],其意义是小于等于i的k的倍数的d数组的和
听上去有点绕口,实际上他就是这么绕口
所以题目就是sum[r/k]-sum[(l-1)/k]
那么我们怎么求出sum呢?l和r可是1e18的鸭!
这时候就要用上牛B的矩阵快速幂
由于在递推过程中,只有到k的倍数才更新sum,所以在到达k的倍数之前,直接就让sum等于原来的值,这样的递推进行k-1次,然后最后一次再进行更新,这样的过程持续n次,我们就能得到小于等于nk长度的所有k的倍数的d数组的和!
所以就是
感觉这题挺有意思的,打call
以下是代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,b) for(ll i=a;i<=b;i++)
#define per(i,a,b) for(ll i=a;i>=b;i--)
#define ll long long
const ll MOD = 1e9+7;
ll n;
void mul(ll f[3],ll a[3][3]){
ll c[3];
memset(c,0,sizeof(c));
rep(j,0,2){
rep(k,0,2){
c[j]=(c[j]+(ll)f[k]*a[k][j])%MOD;
}
}
memcpy(f,c,sizeof(c));
}
void mul2(ll a[3][3],ll b[3][3]){
ll c[3][3];
memset(c,0,sizeof(c));
rep(i,0,2){
rep(j,0,2){
rep(k,0,2){
c[i][j]=(c[i][j]+(ll)a[i][k]*b[k][j])%MOD;
}
}
}
memcpy(a,c,sizeof(c));
}
void mulself(ll a[3][3]){
ll c[3][3];
memset(c,0,sizeof(c));
rep(i,0,2){
rep(j,0,2){
rep(k,0,2){
c[i][j]=(c[i][j]+(ll)a[i][k]*a[k][j])%MOD;
}
}
}
memcpy(a,c,sizeof(c));
}
int main()
{
ll t;
scanf("%I64d",&t);
ll l,r,k;
rep(_,1,t){
scanf("%I64d%I64d%I64d",&l,&r,&k);
ll p[3][3]={{1,0,0},{0,1,0},{0,0,1}};
ll p2[3][3];
ll a[3][3]={{0,1,0},{1,1,0},{0,0,1}};
ll n=k-1;
for(;n;n>>=1){
if(n&1)mul2(p,a);
mulself(a);
}
ll b[3][3]={{0,1,1},{1,1,1},{0,0,1}};
mul2(p,b);
ll f1[3]={1,1,0};
ll f2[3]={1,1,0};
n=r/k;
memcpy(p2,p,sizeof(p));
for(;n;n>>=1){
if(n&1)mul(f1,p);
mulself(p);
}
n=(l-1)/k;
for(;n;n>>=1){
if(n&1)mul(f2,p2);
mulself(p2);
}
ll ans=(f1[2]-f2[2]+MOD)%MOD;
printf("Case %I64d: %I64d\n",_,ans);
}
return 0;
}