蓝桥杯 垒骰子

垒骰子
赌圣atm晚年迷恋上了垒骰子,就是把骰子一个垒在另一个上边,不能歪歪扭扭,要垒成方柱体。
经过长期观察,atm 发现了稳定骰子的奥秘:有些数字的面贴着会互相排斥!
我们先来规范一下骰子:1 的对面是 4,2 的对面是 5,3 的对面是 6。
假设有 m 组互斥现象,每组中的那两个数字的面紧贴在一起,骰子就不能稳定的垒起来。
atm想计算一下有多少种不同的可能的垒骰子方式。
两种垒骰子方式相同,当且仅当这两种方式中对应高度的骰子的对应数字的朝向都相同。
由于方案数可能过多,请输出模10^9 + 7 的结果。
不要小看了 atm 的骰子数量哦~
「输入格式」
第一行两个整数 n m
n表示骰子数目
接下来 m 行,每行两个整数 a b ,表示 a 和 b 数字不能紧贴在一起。
「输出格式」
一行一个数,表示答案模10^9 + 7 的结果。
「样例输入」
2 1
1 2
「样例输出」
544
「数据范围」
对于 30% 的数据:n <= 5
对于 60% 的数据:n <= 100
对于 100% 的数据:0 < n <= 10^9, m <= 36
 
资源约定:
峰值内存消耗 <256M
CPU消耗  < 2000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include<xxx>, 不能通过工程设置而省略常用头文件。
 

提交时,注意选择所期望的编译器类型。


思考过程,拿到该题首先想到可以深度为n的穷举来完成,看到时间限制和数据规模,这可能要跑n年。初步判断,所用时间不可能大于O(n).小白我完全没有思路了,看了别人的答案后,发现了算法真的太神奇了,又深深的爱上了它。这道题用的是矩阵快速幂和我暂时所认为的dp思想,我自己认为中间之所以会用到矩阵的快速幂原因在于DP中的一种特殊形式,该题如果要用dp思想来构建,首先想的是1.以什么样的状态顺序进行  2.底层状态是否可以透明  3.如何表示dp的状态。dp状态的确定一般dp的本身是答案所求,不妨令其为方法数,再考虑用几维的dp,一般不超过2维,先从2维想起吧,那么这么想我可以让假设dp[i][j],其中i为层数,j可以用来表示向上的点数。这样是可以解决的,但是这道题n之大,二维的必须舍弃,然后再想一下,其实前面状态和我现在要讨论的状态其实是可以忽略的,因为都归纳到我现在的要讨论的状态中。所以用一个1维的dp即可。

该题的主要思想是这样的,首先对于4,我们可以再最后乘4的n次幂。用一个1行6列的数组dp[j]来描述前一层点数j朝上时方案的总数(当然要*4的相应的幂数才是),6次累加便可得到总数。现在假设该层朝上的为i,那我得保证i相对的那一面 (i+2)%6+1与我的前一层向上的点数(假设为j)不能是我上面输入的限制,现在假设 (i+2)%6+1与j不能相贴,也就是在我这一层的状态dp记录中,dp[i] 为上一层dp的不包括dp[j]的累加。最后再把该dp[i]确定下来,如此反复需要6*6次。发现没有,在这个过程当中,很像我们的矩阵乘法,加与不加其实是一种乘0或1的选择,这样的一种选择在每一层中都是重复的,所以我们建立起了一个方阵,judge[6][6],其中1表示取,0表示不取。现在的难点落在了如何去构造这样的judge选择矩阵。我们把judge抽象为judge[i][j],dp*judge中,j的数目是决定我们乘出来后的列数,乘出来后的新的dp中j的意义就变为向上的点数,也就是我们在这里要把judge中的j定义为该层向上的点数。那么i要表示什么呢?如果j确定,那么接下来就是dp[k]*judge[k][j]来确定新的dp中的dp[j],可以发现我们这里的i,需要把它定义为上一层向上的点数!!这样judge的定义就结束了,接下来把问题放在在哪放0,综上judge[i][j]表示为该层骰子向上点数为j时,其上一层骰子点数向上为i时,是不是两个骰子会互相排斥,这里(j ,i)和我们输入的排斥对还是不一样的,假设输入的为(a,b),这里需要把j相对应的位置定位(a+2)%6 +1和(b+2)%6 +1。到这里问题基本解题思路就解决得差不多。接下来就是快速幂的算法,可以发现当n层时dp = dp*pow(judge,n-1).在这里就涉及到了矩阵快速幂,在最后乘上pow(4,n)也运用到了整数的快速幂。这道题看了小白我一个晚上,因为在此之前不知道快速幂这种事情,还有judge的定义很费解。看了这么久,发现内容很丰富,有必要总结一波。

接下来是我自己写的代码,按理说应该没问题。有问题,多谢指正。

#include<iostream>
using namespace std;
#define N (int)(1e9 + 7)
#define ll long long 
int test_times = 0;
#define Debug

class Matrix
{
public:
	ll mp[7][7];
	Matrix( int line,int column,const int data)
	{
		int i,j;
		mline = line;
		mcol = column;
		for(i = 1;i <= line;i++)
		{
			for(j = 1;j <= column;j++)
			{
				mp[i][j] = data;
			}
		}
	}
	Matrix(int);
	Matrix(Matrix &);
	Matrix operator *(const Matrix & m1);
	void operator ^=(int n);
	//void operator = (Matrix&);
	friend ostream & operator <<(ostream & out,Matrix &t1);
private:
	
	int mline,mcol;
};
Matrix::Matrix(int line)
{
	int i;
	mline = line;
	mcol = line;
	memset(mp,0,sizeof(mp));
	for(i = 1;i <= line;i++)
	{
		mp[i][i] = 1;
	}
}
Matrix::Matrix(Matrix &t)
{
	mline = t.mline;
	mcol = t.mcol;
	for(int i = 1;i <= t.mline;i++)
	{
		for(int j = 1;j <= t.mcol;j++)
		{
			mp[i][j] = t.mp[i][j];
		}
	}
}

Matrix Matrix::operator *(const Matrix & m1)
{
    int i,j,k;
	Matrix temp(mline,m1.mcol,0);

	if(mcol != m1.mline)
	{
		cout << "error" << endl;
		system("pause");
		exit(0);
    }
    
    for(i = 1;i <= mline;i++)
    {
        for(j = 1;j <= m1.mcol;j++)
        {
            for(k = 1;k <= mcol;k++)
            {
                temp.mp[i][j] = (temp.mp[i][j]+(mp[i][k] * m1.mp[k][j])%N)%N;
            }
        }
    }
	return temp;
}


//快速幂
void Matrix::operator ^=(int n)
{
	Matrix res(6);
	Matrix *t = this;
	while(n)
	{
		if(n & 1)
		{
			res = res * *t;
		}
		n >>=1;
		*t = (*t) * (*t);
	}
	*t = res;
}
/*
void  Matrix::operator =(Matrix &m1)
{
	mline = m1.mline;
	mcol = m1.mcol;
	for(int i = 1;i <= m1.mline;i++)
	{
		for(int j = 1;j <= m1.mcol;j++)
		{
			mp[i][j] = m1.mp[i][j];
		}
	}

}
*/
ostream & operator <<(ostream & out,Matrix &t1)
{
	int i,j;
	for(i = 1;i <= t1.mline;i++)
	{
		for(j = 1;j <= t1.mcol;j++)
		{
			out << t1.mp[i][j] << " ";
		}
		out << endl;
	}
	return out;
}

long long quick_pow(long long int a,long long int b)
{
    ll res = 0;
    while(b)
    {
        if(b&1)
        {
            res += a;
            #ifdef Debug
            test_times++;
            #endif
        }
        a = (a*a)%N;
        b >>= 1;
    }
	return res;
}


void test_1()
{
	Matrix m1(6,6,1);
	Matrix m2(m1);
	m1 = m1*m2;
	cout << m1;
}

void test_2()
{
	Matrix m1(6,6,1);
	Matrix m2(m1);
	m1^=10000000;
	cout << m1;
}


void solve()
{
	int i;
	int n;
	int m;
	ll res = 0;;
	Matrix pos_num(1,6,1);
	Matrix judge(6,6,1);
	cin >> n;
	cin >> m;
	for(i = 1;i<=m;i++)
	{
		int temp_x,temp_y;
		cin >> temp_x >> temp_y;
		judge.mp[temp_x][(temp_y+2)%6 + 1 ] = 0;
		judge.mp[temp_y][(temp_y+2)%6 + 1] = 0;
	}
	judge ^= (n-1);
	pos_num = pos_num * judge;
	for(i = 1;i <= 6;i++)
	{
		res = (res + pos_num.mp[1][i])%N;
	}
	res = (res * quick_pow(4,n))%N;
	cout << res;
}

int main()
{
	//test_1();
	//test_2();
	solve();
	system("pause");
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值