第六届蓝桥杯JavaB组省赛-垒骰子

题目描述

赌圣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

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。

思路:矩阵快速幂+快速幂

这个题我首先想到的是记忆化深搜,但是评测数据的规模让我望而止步。一般遇到规模较大的,还要考虑是不是用动态规划,写完一测试过载了。还是测试数据太大了10的九次方。
日常看大佬的博客,需要用快速幂,可以把时间复杂度降为O(log(n)).
首先快速幂的模板

while(pow!=0)
{
	int ans=1;
	if((pow & 1)==1)
		ans=ans*m;   //m为底数,m的pow次方
	m*=m;
	pow>>=1;
}

将两层之间骰子的匹配转换为矩阵处理:
以骰子底面为准
第六届蓝桥杯JavaB组省赛-垒骰子_第1张图片

第六届蓝桥杯JavaB组省赛-垒骰子_第2张图片

代码

import java.util.Arrays;
import java.util.Scanner;
public class test_01 {
	static int[] dy= {0,4,5,6,1,2,3};   //每个面对应的面
	static long[][] confict=new long[6][6];
	static int m,n;
	static int MOD=1000000007;
	public static void main(String[] args) 
	{
		Scanner in=new Scanner(System.in);
		m=in.nextInt();n=in.nextInt();
		
		//构建冲突矩阵
		for (int i = 0; i < 6; i++) 
			Arrays.fill(confict[i], 1);

		for (int i = 0; i < n; i++) 
		{
			int a=in.nextInt();
			int b=in.nextInt();
			confict[dy[a]-1][b-1]=confict[dy[b]-1][a-1]=0;
		}
		
		//计算冲突矩阵的n-1次幂
		long[][] t=matrix_pow(confict,m-1);
		long sum=0;
		for(int i=0;i<6;i++)
			for(int j=0;j<6;j++)	
				sum=(sum+t[i][j])%MOD;
		System.out.println(sum*pow(4,m)%MOD);
	}
	
	private static long[][] matrix_pow(long[][] confict, int pow) 
	{
		long[][] e=new long[6][6];
		//构造单位矩阵
		for (int i = 0; i < 6; i++) 
			for (int j = 0; j < 6; j++) 
				if(i==j) 
					e[i][j]=1;
				else 
					e[i][j]=0;
		while(pow!=0)
		{
			if((pow&1)==1)
				e=matrix_mult(e, confict);
			confict=matrix_mult(confict, confict);
			pow>>=1;
		}
		return e;
	}
	private static long[][] matrix_mult(long[][] a,long[][] b)
	{
		long[][] t=new long[6][6];
		for (int i = 0; i < 6; i++)
			for (int j = 0; j < 6; j++) 
				for (int k = 0; k < 6; k++) 
					t[i][j]=(t[i][j]+a[i][k]*b[k][j])%MOD;//想乘或者相加,一步一步取余是等同于总共的结果取余
		return t;
	}
	private static long pow(long a,long pow)
	{
		long t=1;
		while(pow!=0)
		{
			if((pow&1)==1)
				t=(t*a)%MOD;   //对于数据a*a 远远大于了int的范围,对于数据规模比较大的题,尽量用long
			a=a*a%MOD;    //虽然说要的是结果取余MOD,但是也可以先取余再乘。
			pow>>=1;
		}
		
		return t;
	}

总结

1.对于数据规模想当大的题,中间的每次运算也要取余,而且要用long型,比如这题就出现:
a=a*a%MOD; a前一次是34564586,取模并没有变小,但是第二次遍历a*a直接超出范围了。a直接等于0了,后面的结果全错了。
2.这种数据规模大的题,写完一定要带一下大点的数据,一定要是试一下!!!!!
3.在写代码的时候,思路乱了,手写出来流程图绝对有效。比如这个题,你把两层的思路手写的过一遍。过着一遍,你的思路就有了。不要懒,一定要手写出来,写在纸上!!!!

你可能感兴趣的:(蓝桥杯,蓝桥杯,java)