cf#307-D. GukiZ and Binary Operations-矩阵快速幂

http://codeforces.com/contest/551/problem/D

给你 n,l,k,m;

题意:你可以任意挑选小于2^l的n个数,让它们以这个公式

计算得到k;要使得 得到的k与给出的k相等,问你有多少种方案数,答案取余m。


首先我们看如何得到k;

我们需要构造n个数

我们先把这n个数转为 L位 二进制数,得到一个0 1 矩阵:

xxxxxxxxxxxxxxxxxxxx

xxxxxxxxxxxxxxxxxxxx

xxxxxxxxxxxxxxxxxxxx

xxxxxxxxxxxxxxxxxxxx

xxxxxxxxxxxxxxxxxxxx

同时我们也把k转为 L位 二进制数:

000000000000111111

我们可以知道,要使得k的第i位为0,则要求 n个数中,不能有相邻的两个数 的 【第i 位】都为1   (如果相邻为1,根据公式推得 k的第i位最终必为 1);

假如我们能求出,这个使ki位0的方案数,我们设有x种情况符合该条件;

并且,由于这n个数的第i位,总共最多只有2的n次方总情况。(每一个数的第i位有2种情况,n个数是2^n种情况)

同样的,如果要使得k的第i位为1,其情况有y=2^n-x种


注意:  

 1、如果这n个数的第i位确定了是111100(从上到下),他与第i-1位完全没关系,相互独立,也就是说,k的每一位我们都可以独立计算,位与位之间并没有关联。  (之前一直误以为是选n个数,如果是选的话,选了n个数使得k的第i位为0,第i-1,i-2,i-3位都会被确定下来,那就无法做下去了。但是我们是构造n个数,构造的话,每一bit之间是没影响的)


接下来是怎么计算   在n 个数 的情况下 ,经过公式计算 能得到 k的第i位为0,也就是n个数在第i位里【没有相邻的两个1】存在;

可发现,这个答案为f(n)=f(n-1)+f(n-2); 恰好是斐波那契数列,由于n太大 10^18//这个证明迟点再写

我们可以用矩阵快速幂来计算 得到 这个x

然后对k的每一位判断,如果是0,则ans*=x;

如果是1,则ans*=y;

最后输出 ans%mod;


/***************************************************************

为什么    一个n长度的零壹数组,每个位置元素随意,要求 不能存在相邻的两个1,求多少种排列,的答案是斐波那契数列?

证明: 

如果n长度的串第一位是0 ,那么后面随便跟,n-1长度的合法串方案书就是f(n-1)
如果n长度的串第一位是1,那么第二位肯定是0,后面随便跟,n-2长度的合法串方案书就是f(n-2)

第一位只有这两种情况,就是f(n)=f(n-1)+f(n-2)

****************************************************************/

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
struct martix
{
	__int64 m[2][2];
};

 
martix unit_matrix;
__int64 mod; 
__int64 len=2;
martix mul(martix a,martix b)
{
	martix ret;
	__int64 i,j,k;
	for (i=0;i<len;i++)
	{
		for (j=0;j<len;j++)
		{
			ret.m[i][j]=0;
			for (k=0;k<len;k++)
			{
				ret.m[i][j]+=a.m[i][k]*b.m[k][j];
				 ret.m[i][j]%=mod;
			}
		}
	}
	return ret;
}
martix pow_m(martix a,__int64 b)
{
	martix ret= unit_matrix;
	while(b)
	{
		if (b&1)
			ret=mul(ret,a);
		a=mul(a,a);
		b>>=1;
	}
	return ret;
}
__int64 pow_m_int(__int64 a,__int64 b)
{
	__int64 ret= 1;
	while(b)
	{
		if (b&1)
			ret=(ret*a)%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ret%mod;
}


int main()
{
	__int64 n,k,l;
	__int64 i,j;
	scanf("%I64d%I64d%I64d%I64d",&n,&k,&l,&mod);
	for(i = 0; i < 2; i++)
            for(j = 0; j < 2; j++)
                unit_matrix.m[i][j] = 0;
	for(i = 0; i < 2; i++)	//初始化单位矩阵
		unit_matrix.m[i][i] = 1;

	if ( pow(2.0,l)<= k)
	{
		printf("0\n");
		return 0;
	} 
	martix x;  
	x.m[0][0]=1; 	x.m[0][1]=1;
	x.m[1][0]=1; 	x.m[1][1]=0;  

	x=pow_m(x,n+1); //n个数的该位有ans种情况使得k的该位为0
    __int64 	xx=x.m[0][0]%mod;
	__int64 one=(pow_m_int(2,n)-xx)%mod; 
	if (one<0)
		one+=mod;
	__int64 sum=1;
	for (i=0;i<l;i++)
	{
		if (k&1)
			sum=(sum*one)%mod;
		else
			sum=sum*xx%mod;

		k>>=1;
	}
 
	printf("%I64d\n",sum%mod);
	
	return 0;
	
}





你可能感兴趣的:(cf#307-D. GukiZ and Binary Operations-矩阵快速幂)