题目描述
赌圣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;
}
将两层之间骰子的匹配转换为矩阵处理:
以骰子底面为准
代码
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.在写代码的时候,思路乱了,手写出来流程图绝对有效。比如这个题,你把两层的思路手写的过一遍。过着一遍,你的思路就有了。不要懒,一定要手写出来,写在纸上!!!!