赌圣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
对于这题我脑海里第一个想到的便是dp状态转移,而若是一个一个筛子进行状态转移在n的数特别大的时候则会运行超时,所以我们多个筛子之间进行状态转移,就好像如果我要算2的10^9次方时,我则会将10^9进行分解,如果可以除二则除二,否则减一,直到数变成1为止,用一个数组来记录操作步骤,同样的我们将筛子数量也进行相同的分解,把乘法变为dp状态转移的操作即可。
dp状态转移的骰子组分为36个情况,骰子组的头部有1到6的情况,尾部也是1到6,所以有6×6=36种,每一次拼接便是把两组骰子的所有情况进行判定:是否可以进行拼接,如果可以便把两边骰子组的当前情况的所有可能数进行相乘。
#include<stdio.h>
void yiban(long long a[][6], long long c[][6])//相同数量的骰子组之间进行拼接
{
long long z[6][6];
int i, j, ii, jj;
for (i = 0; i < 6; i++)
{
for (j = 0; j < 6; j++)
{
z[i][j] = c[i][j]; c[i][j] = 0;
}
}
for (i = 0; i < 6; i++)
{
for (j = 0; j < 6; j++)
{
for (ii = 0; ii < 6; ii++)
{
for (jj = 0; jj < 6; jj++)
{
if (a[j][ii] == 0 && z[i][j] != 0 && z[ii][jj] != 0)
{
c[i][jj] = (c[i][jj] + z[i][j] * z[ii][jj]) % 1000000007;
}
}
}
}
}
}//i、j代表上面的骰子组,ii、jj代表下面的骰子组,下面的yige函数同理
void yige(long long a[][6], long long b[][6], long long c[][6])/*与只有一个骰子的骰子组进行拼接*/
{
long long z[6][6];
int i, j, ii, jj;
for (i = 0; i < 6; i++)
{
for (j = 0; j < 6; j++)
{
z[i][j] = c[i][j]; c[i][j] = 0;
}
}
for (i = 0; i < 6; i++)
{
for (j = 0; j < 6; j++)
{
for (ii = 0; ii < 6; ii++)
{
for (jj = 0; jj < 6; jj++)
{
if (a[j][ii] == 0 && z[i][j] != 0 && z[ii][jj] != 0)
{
c[i][jj] = (c[i][jj] + b[ii][jj] * z[i][j]) % 1000000007;
}
}
}
}
}
}
int main()
{
int i, j;
long long n, m, a[6][6] = { 0 }, b[6][6] = { 0 }, aa, bb, c[6][6] = { 0 }, d[1000] = { 0 }, q;/*d数组记录分解步骤,a数组记录不可对面,b数组代表单个骰子的的所有可能,c数组代表当前最大骰子组的所以可能*/
for (i = 0; i < 6; i++)
{
for (j = 0; j < 6; j++)
{
a[i][j] = 0; b[i][j] = 0; c[i][j] = 0;
}
}//数组初始化现在看好像没什么用
scanf("%lld%lld", &n, &m);
for (i = 0; i < m; i++)
{
scanf("%lld%lld", &aa, &bb);
a[aa - 1][bb - 1] = 1; a[bb - 1][aa - 1] = 1;
}//a数组的记录
b[0][3] = 4; b[1][4] = 4; b[2][5] = 4; b[3][0] = 4; b[4][1] = 4; b[5][2] = 4;
c[0][3] = 4; c[1][4] = 4; c[2][5] = 4; c[3][0] = 4; c[4][1] = 4; c[5][2] = 4;//b、c数组的初始化,此时都为一个骰子
for (q = 0; n != 1; q++)
{
if (n % 2 == 0)
{
n /= 2; d[q] = 2;
}
else
{
n -= 1; d[q] = 1;
}
}//d数组的记录
q--;
for (q; q >= 0; q--)
{
if (d[q] == 2) yiban(a, c);
else if (d[q] == 1) yige(a, b, c);
}
q = 0;
for (i = 0; i < 6; i++)
{
for(j = 0; j < 6; j++)
{
q = (q + c[i][j]) % 1000000007;
}
}//将最后得到的c数组的所有面的所有可能相加既是最终答案
printf("%lld", q);
return 0;
}
本人知识欠缺,若有不对或者与其他作者过于相同,敬请原谅。(狗头保命)