【2016 ACM-ICPC 曼谷区域赛 Gym-101161 G】Binary Strings【矩阵快速幂】

本文探讨了如何计算满足特定条件的01串数量,即长度位于[L,R]区间内,长度为K的倍数,且不含连续的111。采用动态规划与矩阵快速幂相结合的方法解决这一难题。

题意:

010101串,不包含两个连续的 111。要求给出满足下述三个条件的 010101 串的个数。(答案模 1e9+71e9+71e9+7)

  • 010101串长度在 [L,R][L,R][L,R] 之间 (1≤L≤R≤1018)(1\leq L\leq R\leq 10^{18})(1LR1018)
  • 010101串长度是 KKK 的倍数 (1≤K≤109)(1\leq K\leq 10^9)(1K109)
  • 不包含连续两个111010101

思路:

dp[i][0/1]dp[i][0/1]dp[i][0/1] 表示长度为iii,末尾为0/10/10/1的字符串个数。
dp[i+1][0]=dp[i][0]+dp[i][1],dp[i+1][1]=dp[i][0]dp[i+1][0]=dp[i][0]+dp[i][1],dp[i+1][1]=dp[i][0]dp[i+1][0]=dp[i][0]+dp[i][1],dp[i+1][1]=dp[i][0]

通过打表,可以发现 dp[i]dp[i]dp[i] 是一个斐波那契数列,dp[1]=2,dp[2]=3,dp[3]=5,dp[n]=dp[n−1]+dp[n−2]dp[1]=2,dp[2]=3,dp[3]=5,dp[n]=dp[n-1]+dp[n-2]dp[1]=2,dp[2]=3,dp[3]=5,dp[n]=dp[n1]+dp[n2]。(也可以直接推导得到该性质)

因此先通过斐波那契数列的常规求解方式求出 dp[k]dp[k]dp[k]dp[k−1]dp[k-1]dp[k1]。再求出 sum[x]sum[x]sum[x]sum[x]sum[x]sum[x] 表示长度小于等于xxx,且长度是 KKK 倍数的 010101 串总数。

假设 aaa 是最大的小于 LLLKKK 的倍数,bbb 是最大的小于 RRR 且大于 LLLKKK 的倍数,则 ans=sum[b]−sum[a]ans=sum[b]-sum[a]ans=sum[b]sum[a]

(dp[i−1]dp[i]sum[i])∗(011111001)=(dp[i]dp[i+1]sum[i+1]) \begin{pmatrix} dp[i-1] & dp[i] & sum[i] \end{pmatrix}*\begin{pmatrix} 0 & 1 & 1\\ 1 & 1 & 1\\ 0 & 0 & 1\end{pmatrix}=\begin{pmatrix} dp[i] & dp[i+1] & sum[i+1] \end{pmatrix} (dp[i1]dp[i]sum[i])010110111=(dp[i]dp[i+1]sum[i+1])

但是 sum[x]sum[x]sum[x] 只表示长度为 KKK 倍数的 010101 串的总数,因此仅当 dp[i]dp[i]dp[i]iiiKKK 的倍数时,dp[i]dp[i]dp[i] 才会被累加到答案 sum[i]sum[i]sum[i] 中。

(dp[k−1]dp[k]sum[k])∗(010110001)k−1∗(011111001)=(dp[2∗k−1]dp[2∗k]sum[2∗k]) \begin{pmatrix} dp[k-1] & dp[k] & sum[k] \end{pmatrix}*\begin{pmatrix} 0 & 1 & 0\\ 1 & 1 & 0\\ 0 & 0 & 1\end{pmatrix}^{k-1}*\begin{pmatrix} 0 & 1 & 1\\ 1 & 1 & 1\\ 0 & 0 & 1\end{pmatrix}=\begin{pmatrix} dp[2*k-1] & dp[2*k] & sum[2*k] \end{pmatrix} (dp[k1]dp[k]sum[k])010110001k1010110111=(dp[2k1]dp[2k]sum[2k])

此处 sum[2∗k]=sum[k]+dp[2∗k]sum[2*k]=sum[k]+dp[2*k]sum[2k]=sum[k]+dp[2k],因此先矩阵快速幂求出 k−1k-1k1 阶的矩阵,乘上后面的 111 阶矩阵,得到矩阵 basebasebase,再对 basebasebase 进行快速幂,求出 sum[a]sum[a]sum[a]sum[b]sum[b]sum[b],即可得到答案。


总结:

此题的主要难点应该在于打表,如果不能发现 dpdpdp 数组是斐波那契数组的话,还是非常难以进行后续矩阵快速幂的推导的。

注意矩阵快速幂的矩阵乘积形式一定要写正确,比赛时将左边的横矩阵写成了列矩阵,导致一直 WaWaWa,很是痛心!


代码:

代码改来改去的,非常丑陋,不具有借鉴价值…

#include <cstdio>
#include <iostream>
#include <cstring>
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
using namespace std; 
const int mod = 1e9+7;
typedef long long ll;
 
ll L,R,K;
//i、k、j只能作为临时变量 
struct Matrix{
	ll a[10][10];
	Matrix(){memset(a,0,sizeof(a));}
	
	Matrix operator * (const Matrix y)
	{
		Matrix ans;
		for(int i = 1;i <= 3;i++)   //行 
			for(int j = 1;j <= 3;j++)  //列 
				for(int k = 1;k <= 3;k++)
					ans.a[i][j] = (ans.a[i][j]+a[i][k]*y.a[k][j]%mod)%mod;			
		return ans;
	}
};
 
Matrix q_pow(Matrix x,ll k)
{
	Matrix ans = x; k--;
	while(k > 0)
	{
		if(k&1)
			ans = ans*x;
		x = x*x;
		k = k>>1;
	}
	return ans;
}
 
ll solve(ll a1,ll a2,ll sum1, Matrix x,ll n){ //求sum[k], n >= 2
	Matrix tp = q_pow(x,n-1ll);
	ll ans = ((tp.a[1][3]*a1%mod+tp.a[2][3]*a2%mod)%mod+tp.a[3][3]*sum1%mod)%mod;
	return ans;
}
 
int main()
{
	int _; scanf("%d",&_);
	rep(Ca,1,_)
	{
		scanf("%lld%lld%lld",&L,&R,&K);
		Matrix p;
		for(int i = 1;i <= 3;i++)
			for(int j = 1;j <= 3;j++)
				p.a[i][j] = 1;
		p.a[1][1] = p.a[3][1] = p.a[3][2] = 0;
		ll a1 = 2, a2 = 3, sum1 = 5;
		if(K >= 3){
			Matrix tmp = q_pow(p,K-2ll);
			a1 = ((tmp.a[1][1]*2ll%mod+tmp.a[2][1]*3ll%mod)%mod+tmp.a[3][1]*5ll%mod)%mod;
			a2 = ((tmp.a[1][2]*2ll%mod+tmp.a[2][2]*3ll%mod)%mod+tmp.a[3][2]*5ll%mod)%mod;
			sum1 = a2;
		}
		else if(K == 1){
			ll ans1 = 0, ans2 = 0;
			if(L == 1) ans1 = 0;
			else if(L == 2) ans1 = 2;
			else if(L == 3) ans1 = 5;
			else{
				Matrix tmp = q_pow(p,L-3);
				ans1 = ((tmp.a[1][3]*2ll%mod+tmp.a[2][3]*3ll%mod)%mod+tmp.a[3][3]*5ll%mod)%mod;
			}
 
			if(R == 1) ans2 = 2;
			else if(R == 2) ans2 = 5;
			else{
				Matrix tmp = q_pow(p,R-2);
				ans2 = ((tmp.a[1][3]*2ll%mod+tmp.a[2][3]*3ll%mod)%mod+tmp.a[3][3]*5ll%mod)%mod;
			}
			printf("Case %d: %lld\n",Ca,(ans2-ans1+mod)%mod);
			continue;
		}
		else sum1 = 3;
 
		Matrix tp1,tp2;
		for(int i = 1;i <= 3;i++)
			for(int j = 1;j <= 3;j++)
				tp1.a[i][j] = 1;
		tp1.a[1][1] = tp1.a[3][1] = tp1.a[3][2] = tp1.a[1][3] = tp1.a[2][3] = 0ll;
 
		for(int i = 1;i <= 3;i++)
			for(int j = 1;j <= 3;j++)
				tp2.a[i][j] = 1;
		tp2.a[1][1] = tp2.a[3][1] = tp2.a[3][2] = 0;
 
		Matrix base = q_pow(tp1,K-1ll)*tp2;	
		ll hp1,hp2;
		hp1 = (L/K)*K;
		if(hp1 == L) hp1 = (L/K-1ll)*K;
 
		hp2 = R/K;
		ll ans1 = 0ll,ans2 = 0ll;
		if(hp2 > 1ll){
			ans1 = solve(a1,a2,sum1,base,hp2);
		}
		else if(hp2 == 1) ans1 = sum1;
 
		if(hp1 > K) ans2 = solve(a1,a2,sum1,base,hp1/K);
		else if(hp1 == K) ans2 = sum1;
		// LOG2("ans1",ans1,"ans2",ans2);
		printf("Case %d: %lld\n",Ca,(ans1-ans2+mod)%mod);
	}
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gene_INNOCENT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值