“红色病毒“问题

题目描述:
医学界发现的新病毒因其蔓延速度和Internet上传播的"红色病毒"不相上下,被称为"红色病毒",经研究发现,该病毒及其变种的DNA的一条单链中,胞嘧啶,腺嘧啶均是成对出现的。
现在有一长度为N的字符串,满足一下条件:
(1) 字符串仅由A,B,C,D四个字母组成;
(2) A出现偶数次(也可以不出现);
(3) C出现偶数次(也可以不出现);
计算满足条件的字符串个数.
当N=2时,所有满足条件的字符串有如下6个:BB,BD,DB,DD,AA,CC.
由于这个数据肯能非常庞大,你只要给出最后两位数字即可.

Input
每组输入的第一行是一个整数T,表示测试实例的个数,下面是T行数据,每行一个整数N(1<=N<2^64),当T=0时结束.

Output
对于每个测试实例,输出字符串个数的最后两位,每组输出后跟一个空行.
Sample Input
4
1
4
20
11
3
14
24
6
0
Sample Output
Case 1: 2
Case 2: 72
Case 3: 32
Case 4: 0

Case 1: 56
Case 2: 72
Case 3: 56

如果没有任何条件限制,A、B、C、D组成长度为n的字符串,其个数应该为:4n
因为有了A、C需要出现偶数次的要求,就出现合法和不合法的不同分组。
在不合法的组里,又有
1.A出现奇数次、C出现偶数次;
2.C出现奇数次、A出现偶数次;
3.A出现奇数次、C出现奇数次;

我们用数组
f[n][0]保存长度为n,合法的字符串的个数。
f[n][1]保存长度为n,仅A出现奇数次的字符串的个数。
f[n][2]保存长度为n,仅C出现奇数次的字符串的个数。
f[n][3]保存长度为n,A、C出现奇数次的字符串的个数。

f[n][0]
长度为n-1的合法字符串在末尾加上一个B或者D,都可以变成长度为n的合法字符串。
长度为n-1的仅A出现奇数次的字符串再在末尾加上一个A,也可以变成合法字符串。
长度为n-1的仅C出现奇数次的字符串再在末尾加上一个C,也可以变成合法字符串。
所以,f[n][0] = 2 × f[n-1][0] + f[n-1][1] + f[n-1][2];

f[n][1]
长度为n-1的合法字符串在末尾加上A,都可以变成长度为n的仅A出现奇数次的字符串。
长度为n-1的仅A出现奇数次的字符串再在末尾加上一个B或者D,也可以变成仅A出现奇数次的字符串。
长度为n-1的A、C出现奇数次的字符串再在末尾加上一个C,也可以变成仅A出现奇数次的字符串。
所以,f[n][1] = 2 × f[n-1][1] + f[n-1][0] + f[n-1][3];

f[n][2]
长度为n-1的合法字符串在末尾加上C,都可以变成长度为n的仅C出现奇数次的字符串。
长度为n-1的仅C出现奇数次的字符串再在末尾加上一个B或者D,也可以变成仅C出现奇数次的字符串。
长度为n-1的A、C出现奇数次的字符串再在末尾加上一个A,也可以变成仅C出现奇数次的字符串。
所以,f[n][2] = 2 × f[n-1][2] + f[n-1][0] + f[n-1][3];

f[n][3]
长度为n-1的A、C出现奇数次的字符串在末尾加上一B或者D,都可以变成长度为n的A、C出现奇数次的字符串。
长度为n-1的仅A出现奇数次的字符串再在末尾加上一个C,也可以变成A、C出现奇数次的字符串。
长度为n-1的仅C出现奇数次的字符串再在末尾加上一个A,也可以变成A、C出现奇数次的字符串。
所以,f[n][3] = 2 × f[n-1][3] + f[n-1][1] + f[n-1][2];

综上所述,我们得到:
f[i][0]=f[i−1][0]∗2+f[i−1][1]+f[i−1][2];①
f[i][1]=f[i−1][1]∗2+f[i−1][0]+f[i−1][3];②
f[i][2]=f[i−1][2]∗2+f[i−1][0]+f[i−1][3];③
f[i][3]=f[i−1][3]∗2+f[i−1][1]+f[i−1][2];④
初始状态:
f[0][0] = 2,f[0][1] = 1,f[0][2] = 1,f[0][3] = 0;
推导一次可得:
f[1][0] = 6,f[1][1] = 4,f[1][2] = 4,f[1][3] = 2;

1、第一种方法:矩阵快速幂
一行四列矩阵要得到一行四列矩阵,由性质可得需要四行四列矩阵,
再由①②③④式可得变换矩阵T为
2 1 1 0
1 2 0 1
1 0 2 1
0 1 1 2
即 F[0]*T=F[1],F[2]*T=F[3],F[3]=F[0]*T2,
以此类推可得F[n]=F[0]*Tn-1

至此可以看出,只需要知道T的n-1次方,即可算出n字符满足条件的个数F[n][0],而T的n-1次方的计算就可以用到矩阵快速幂的算法,模板为:

void quick_mod(ll n)	//矩阵快速幂 
{
	while(n)
    {
        if(n&1)
        {
            for(int i=0;i<4;i++)
            {
                for(int j=0;j<4;j++)
                {
                    ch[i][j]=0;
                    for(int k=0;k<4;k++)
                    {
                        ch[i][j]+=(ans[i][k]*t[k][j])%100;
                    }
                }
            }
            for(int i=0;i<4;i++)
                for(int j=0;j<4;j++)
                    ans[i][j]=ch[i][j];
            n--;
        }
        n>>=1;
        for(int i=0;i<4;i++)
        {
            for(int j=0;j<4;j++)
            {
                ch[i][j]=0;
                for(int k=0;k<4;k++)
                {
                    ch[i][j]+=(t[i][k]*t[k][j])%100;
                }
            }
        }
        for(int i=0;i<4;i++)
            for(int j=0;j<4;j++)
                t[i][j]=ch[i][j];
    }
    return ;
}

最后附上AC代码(注意要开long long,为什么?因为不开TLE了 ! T T):

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;
typedef long long ll; 
ll t[4][4],ans[4][4],ch[4][4];
//t矩阵为转移变换矩阵,ans矩阵为快速幂的结果矩阵,ch矩阵为中间交换矩阵 

void init()
{
	memset(t,0,sizeof(t));
	memset(ans,0,sizeof(ans));
	//进行矩阵快速幂后矩阵值改变,每一组测试之前都要初始化 
	for(int i=0;i<4;i++)
		ans[i][i]=1;		//初始化单位矩阵
	t[0][0]=2,t[1][0]=1,t[2][0]=1,t[3][0]=0;
    t[0][1]=1,t[1][1]=2,t[2][1]=0,t[3][1]=1;
    t[0][2]=1,t[1][2]=0,t[2][2]=2,t[3][2]=1;
    t[0][3]=0,t[1][3]=1,t[2][3]=1,t[3][3]=2;
    //构造变换矩阵 
    return ;
}
void quick_mod(ll n)	//矩阵快速幂 
{
	while(n)
    {
        if(n&1)
        {
            for(int i=0;i<4;i++)
            {
                for(int j=0;j<4;j++)
                {
                    ch[i][j]=0;
                    for(int k=0;k<4;k++)
                    {
                        ch[i][j]+=(ans[i][k]*t[k][j])%100;
                    }
                }
            }
            for(int i=0;i<4;i++)
                for(int j=0;j<4;j++)
                    ans[i][j]=ch[i][j];
            n--;
        }
        n>>=1;
        for(int i=0;i<4;i++)
        {
            for(int j=0;j<4;j++)
            {
                ch[i][j]=0;
                for(int k=0;k<4;k++)
                {
                    ch[i][j]+=(t[i][k]*t[k][j])%100;
                }
            }
        }
        for(int i=0;i<4;i++)
            for(int j=0;j<4;j++)
                t[i][j]=ch[i][j];
    }
    return ;
}
int main()
{
	while(1)
	{
		ll t;
		cin>>t;
		if(t==0)
			break;
		for(int i=1;i<=t;i++)
		{
			ll n;
			cin>>n;
			init();			//矩阵初始化 
			quick_mod(n-1);	//矩阵快速幂 
			cout<<"Case "<<i<<": "<<(2*ans[0][0]%100+ans[1][0]%100+ans[2][0]%100)%100<<endl;
			//最后要求满足条件的个数,所以只需要打印 F(n)[0][0]=F(1)*(T的n-1次方的第一列)
			//另外题目要求只输出最后两位,所以要模100 
		}
		cout<<endl;		//每组测试数据要进行换行 
	}
	return 0;
}

2、第二种方法:快速幂
变换的四个推导式
f[i][0]=f[i−1][0]∗2+f[i−1][1]+f[i−1][2];①
f[i][1]=f[i−1][1]∗2+f[i−1][0]+f[i−1][3];②
f[i][2]=f[i−1][2]∗2+f[i−1][0]+f[i−1][3];③
f[i][3]=f[i−1][3]∗2+f[i−1][1]+f[i−1][2];④
初始状态:
f[0][0] = 2,f[0][1] = 1,f[0][2] = 1,f[0][3] = 0;

分析四个式子以及初始状态发现:
(1)②③式不同之处为f[i−1][1]∗2与f[i−1][2]∗2,而因为f[0][1] = 1,f[0][2] = 1,所以②③的初始状态相同,所以在经过变化之后结果仍然相同,即f[i][1]=f[i][2]。
(2)①④左右两边相加可得
f[i][0]+f[i][3]=f[i−1][0]∗2+f[i−1][1]*2+f[i−1][2]*2+f[i−1][3]∗2
②③左右两边相加可得
f[i][1]+f[i][2]=f[i−1][0]*2+f[i−1][1]∗2+f[i−1][1]∗2+f[i−1][3]*2
规律为f[i][0]+f[i][3]=f[i][1]+f[i][2],而A、B、C、D组成长度为n的字符串,其个数应该为:4n
∵f[n][0] + f[n][1] + f[n][2] + f[n][3] = 4n
∴f[n][0] + f[n][3] = f[n][1] + f[n][2] = 2 × 4(n-1)
∴f[n-1][1] + f[n-1][2] = 2 × 4(n-2)
∴f[n][0] = 2 × f[n-1][0] + f[n-1][1] + f[n-1][2] = 2 × f[n-1][0] + 2 × 4(n-2)= 2 × f[n-1][0] + 2(2n-3)
继续推导:
f[n][0] = 2 × f[n-1][0] + 2(2n-3)
f[n-1][0] = 2 × f[n-2][0] + 2(2n-5)

f[n-m][0] = 2 × f[n-m-1][0] + 2(2n-2m-3)

f[2][0] = 2 × f[1][0] + 21
f[1][0] = 2

∴f[n][0]
= 2 × f[n-1][0] + 2(2n-3)
=22 × f[n-2][0]+2(2n-4)+2(2n-3)

= 2m × f[n-m][0] + 22(n-m)-1+m-1+ … + 2(2n-3)
= 2n-1 × f[1][0] + 2n-1 + 2n +… + 2(2n-3)
代入f[1][0] = 2
∴f[n][0] = 2n + 2(n-1) + 2n +… + 2(2n-3) = 2(2n-2) + 2(n-1)=4(n-1)+2(n-1)
PS最后一项的解释:第一项乘上初始值即为2n,后面为等比数列求和,第一项为2n-1,末项为2(2n-3),结果=2n+{2(n-1)-2(2n-3)*2}/(1-2)=2×2n-1+2(2n-2)-2(n-1)=2(2n-2)+2(n-1)=4(n-1)+2(n-1)
故要求f[n][0]只需要求4(n-1)+2(n-1)即可,而求ab%k,需要快速幂即可完成,快速幂模板如下:

ll quick_mod(ll a, ll b, ll p)//快速幂:a^b%p
{
	ll ans=1;
	a%=p;
    while(b)
    {
        if(b&1)
    	{
            ans=ans*a%p;
            b--;
        }
        b>>=1;
        a=a*a%p;
    }
    return ans;
}

如下附AC代码:

#include<iostream>

using namespace std;
typedef long long ll;

ll quick_mod(ll a, ll b, ll p)//快速幂:a^b%p
{
	ll ans=1;
	a%=p;
    while(b)
    {
        if(b&1)
    	{
            ans=ans*a%p;
            b--;
        }
        b>>=1;
        a=a*a%p;
    }
    return ans;
}
int main()
{
	while(1)
	{
		ll t;
		cin>>t;
		if(t==0)
			break;
		for(ll i=1;i<=t;i++)
		{
			ll n;
			cin>>n;
			cout<<"Case "<<i<<": "<<(quick_mod(4,n-1,100)+quick_mod(2,n-1,100))%100<<endl;
		}
		cout<<endl;
	}
	return 0;
}

参考解析:

https://blog.csdn.net/wongson/article/details/4029863

完结撒花★,°:.☆( ̄▽ ̄)/$:.°★

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值