poj 2440 DNA (dp|递推|找规律|矩阵乘法)

DNA
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 3644 Accepted: 1429

Description

A kind of virus has attacked the X planet, and many lives are infected. After weeks of study, The CHO (Creature Healthy Organization) of X planet finally finds out that this kind of virus has two kind of very simple DNA, and can be represented by 101 and 111. Unfortunately, the lives on the planet also have DNA formed by 0s and 1s. If a creature's DNA contains the virus' DNA, it will be affected; otherwise it will not. Given an integer L, it is clear that there will be 2 ^ L different lives, of which the length of DNA is L. Your job is to find out in the 2 ^ L lives how many won't be affected?

Input

The input contains several test cases. For each test case it contains a positive integer L (1 <= L <= 10 ^ 8). The end of input is indicated by end-of-file.

Output

For each test case, output K mod 2005, here K is the number of lives that will not be affected.

Sample Input

4

Sample Output

9

Source

POJ Monthly,Static

[Submit]   [Go Back]   [Status]   [Discuss]


题解:dp|递推|找规律|矩阵乘法

算法一:自己比较傻逼,想到的是分块+dp。

10^8显然不能直接dp,于是我们考虑分块,那么预处理每个块和暴力最后一个不完整的块就变成了O(10000*2^5)

我们先想暴力dp,10^4,f[i][j][k][a][b]表示以j,k开头当前推到第i位,后两位位a,b。

这样就可以将n/sqrt(n)各块用dp的方式连接。

g[i][a][b]表示当前在第i个块,并且以a,b结尾。因为预处理的时候记录了开头两位,所以判断是否能够相连,就得到递推式 g[i][a][b]=(g[i][a][b]+g[i-1][x][y]*f[sqrt(n)][j][k][a][b])%p ,需要判断(y,j,k)(x,y,j)是否合法。 

然后我们在得到的g数组的基础上暴力dp将残余块求解即可。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define p 2005
using namespace std;
int n,m;
int f[10003][2][2][2][2],g[10003][2][2],ans[10003][2][2];
int pd(int a,int b,int c)
{
	if (a==1&&b==0&&c==1) return false;
	if (a==1&&b==1&&c==1) return false;
	return true;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	while (scanf("%d",&n)!=EOF)
	{
		m=sqrt(n); 
		if (n==1) {
			cout<<"2"<<endl;
			continue;
		}
		if (n==2){
			cout<<"4"<<endl;
			continue;
		}
		if (n==3){
			cout<<"6"<<endl;
			continue;
		}
		memset(f,0,sizeof(f));
		for (int i=0;i<=1;i++)
		 for (int j=0;j<=1;j++) f[2][i][j][i][j]=1;
		for (int i=0;i<=1;i++)
		 for (int j=0;j<=1;j++)
		  for (int k=0;k<=1;k++) if (pd(i,j,k)) f[3][i][j][j][k]=1;
		for (int i=0;i<=1;i++)
		 for (int j=0;j<=1;j++)
		  for (int a=0;a<=1;a++)
		   for (int b=0;b<=1;b++)
		     if (pd(i,j,a)&&pd(j,a,b))  f[4][i][j][a][b]=1;
		int x=1;
		for (int i=5;i<=m;i++)
		 for (int j=0;j<=1;j++)
		  for (int k=0;k<=1;k++)
		   for (int a=0;a<=1;a++)
		    for (int b=0;b<=1;b++)
		     for (int c=0;c<=1;c++)
		     {
		     	if (pd(a,b,c))
		     	 f[i][j][k][b][c]=(f[i][j][k][b][c]+f[i-1][j][k][a][b])%p;
			 }
		int t=n/m; 
		memset(g,0,sizeof(g));
		for (int i=0;i<=1;i++)
		 for (int j=0;j<=1;j++) 
		  for (int a=0;a<=1;a++)
		   for (int b=0;b<=1;b++)
		      g[1][i][j]=(g[1][i][j]+f[m][a][b][i][j])%p;
		for (int i=1;i<=t-1;i++)
		 for (int x=0;x<=1;x++)
		  for (int y=0;y<=1;y++)
			for (int j=0;j<=1;j++)
			 for (int k=0;k<=1;k++)
			  for (int a=0;a<=1;a++)
			   for (int b=0;b<=1;b++){
			   	if (pd(x,y,j)&&pd(y,j,k))
			   	 g[i+1][a][b]=(g[i+1][a][b]+g[i][x][y]*f[m][j][k][a][b])%p;
			   }
		int size=n-t*m;
		memset(ans,0,sizeof(ans));
		for (int i=0;i<=1;i++)
		 for(int j=0;j<=1;j++)
		  ans[1][i][j]=g[t][i][j]%p;
		for (int i=2;i<=size+1;i++)
		 for (int j=0;j<=1;j++)
		  for (int k=0;k<=1;k++)
		   for (int a=0;a<=1;a++)
		    {
		    	if (pd(j,k,a))  ans[i][k][a]=(ans[i][k][a]+ans[i-1][j][k])%p;
			}     
		int tot=0;
		for(int i=0;i<=1;i++)
		 for (int j=0;j<=1;j++)
		  tot=(tot+ans[(size+1)][i][j])%p;
		printf("%d\n",tot);
    }
}


算法二:

先写个暴力代码打印一下前面几项:
输入 输出 规 律
1 ---> 2 1*2
2 ---> 4 2*2
3 ---> 6 2*3
4 ---> 9 3*3
5 ---> 15 3*5
6 ---> 25 5*5
7 ---> 40 5*8
8 ---> 64 8*8
9 ---> 104 8*13
10 ---> 169 13*13
不难发现规律为斐波拉契数列:1 2 3 5 8 13 21 。。。。中间的相邻两项 或者 和本身的乘积
并且可以发现对2005取膜后,循环节是200
(f[(n/2+n%2)%200]*f[n/2%200])%2005//f[i]表示斐波那契数列的第i项


算法三:矩阵乘法

蓝线为合法递推,红线为不合法递推,根据图构造矩阵,进行矩阵乘法。
 

算法四: 
找递推式f[n]=f[n-1]+f[n-3]+f[n-4]

f[n-1] 表示长度为i-1的序列的合法方案数,如果n-1是合法的那么当前位n上一定可以填0,那么这样就计算出了所有在当前位填0的合法方案数。

因为填0的都已经计算过了,所以我们在来考虑填1的情况。

f[n-3] 表示长度为n-3的序列的合法方案数,n-3与n之间相隔了两位,这两位共有4种选择01,00,11,10,只有00 这一种选择无论第n-3位为0,还是1 ,第n位填1一定成立。

f[n-4] 表示长度为n-4的序列的合法方案数,n-4与n之间相隔了三位,这三位共有8中选择,出去不合法的选择,还剩三种000,100,001  我们可以发现如果选择000,100,的话与第n位的1,构成的合法序列的结尾都是001与f[n-3]的情况相同,所以只能选择001这一种填法。

找到这个递推式,可以找循环节,也可以构造矩阵进行矩乘。
像斐波那契数列这样的递推式,在模意义下,一定是存在循环节的,如果f[i]=f[j],f[i+1]=f[j+1]那么显然会循环,时间复杂度不超过O(mod^2)


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值