題目
賭圣atm晚年迷戀上了壘骰子,就是把骰子一個壘在另一個上邊,不能歪歪扭扭扛施,要壘成方柱體。
經(jīng)過長期觀察抢呆,atm 發(fā)現(xiàn)了穩(wěn)定骰子的奧秘:有些數(shù)字的面貼著會互相排斥煮嫌!
我們先來規(guī)范一下骰子:1 的對面是 4,2 的對面是 5抱虐,3 的對面是 6。
假設(shè)有 m 組互斥現(xiàn)象饥脑,每組中的那兩個數(shù)字的面緊貼在一起恳邀,骰子就不能穩(wěn)定的壘起來。 atm想計(jì)算一下有多少種不同的可能的壘骰子方式灶轰。
兩種壘骰子方式相同谣沸,當(dāng)且僅當(dāng)這兩種方式中對應(yīng)高度的骰子的對 應(yīng)數(shù)字的朝向都相同。
由于方案數(shù)可能過多笋颤,請輸出模 10^9 + 7 的結(jié)果乳附。
不要小看了 atm 的骰子數(shù)量哦~
「輸入格式」
第一行兩個整數(shù) n m
n表示骰子數(shù)目
接下來 m 行内地,每行兩個整數(shù) a b ,表示 a 和 b 不能緊貼在一起赋除。
「輸出格式」
一行一個數(shù)阱缓,表示答案模 10^9 + 7 的結(jié)果。
「樣例輸入」
2 1
1 2
「樣例輸出」
544
「數(shù)據(jù)范圍」
對于 30% 的數(shù)據(jù):n <= 5
對于 60% 的數(shù)據(jù):n <= 100
對于 100% 的數(shù)據(jù):0 < n <= 10^9, m <= 36
資源約定:
峰值內(nèi)存消耗(含虛擬機(jī)) < 256M
CPU消耗 < 2000ms
請嚴(yán)格按要求輸出举农,不要畫蛇添足地打印類似:“請您輸入...” 的多余內(nèi)容荆针。
所有代碼放在同一個源文件中,調(diào)試通過后颁糟,拷貝提交該源碼航背。
注意:不要使用package語句。不要使用jdk1.7及以上版本的特性棱貌。
注意:主類的名字必須是:Main玖媚,否則按無效代碼處理。
分析
經(jīng)過仔細(xì)的分析婚脱,會發(fā)現(xiàn)這是一道DP動態(tài)規(guī)劃
問題今魔,可以先假設(shè)骰子的側(cè)面是固定的,然后通過舉例如下:
輸入:
2 1
1 2
此時(shí)的dp矩陣數(shù)組如下:
| | 1 | 2 | 3 | 4 | 5 | 6 |
| ---- | ---- | ---- | ---- | ---- | ---- | ---- |
| 二層 | 5 | 5 | 6 | 6 | 6 | 6 |
輸入:
3 1
1 2
此時(shí)的dp矩陣數(shù)組如下:
| | 1 | 2 | 3 | 4 | 5 | 6 | sum |
| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
| 二層 | 5 | 5 | 6 | 6 | 6 | 6 |34 |
| 三層 | 28 | 28 | 34 | 34 | 34 | 34 |192 |
具體變換過程如下:
| | 1 | 2 | 3 | 4 | 5 | 6 | sum |
| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
| 1 | 5 | 5 | 6 | 6 | - | 6 |28 |
| 2 | 5 | 5 | 6 | - | 6 | 6 |28 |
| 3 | 5 | 5 | 6 | 6 | 6 | 6 |34 |
| 4 | 5 | 5 | 6 | 6 | 6 | 6 |34 |
| 5 | 5 | 5 | 6 | 6 | 6 | 6 |34 |
| 6 | 5 | 5 | 6 | 6 | 6 | 6 |34 |
從中分析可以發(fā)現(xiàn)當(dāng)n=3時(shí)起惕,每種情況都使用到了n=2時(shí)的數(shù)據(jù)涡贱,出現(xiàn)重疊子問題與最優(yōu)子結(jié)構(gòu),于是用DP
來求解惹想。同時(shí)问词,為了節(jié)省空間,可以使用滾動DP
來替代DP
嘀粱。
最后激挪,由于側(cè)面方案數(shù)為4,那么在乘以4^n就可以了就可得到最終解锋叨。
代碼
import java.math.BigInteger;
import java.util.Scanner;
public class Nine {
public static final int MOD = 1000000007;
public static int init[] = { -1, 4, 5, 6, 1, 2, 3 }; // 骰子對面
public static boolean conflict[][] = new boolean[7][7]; // 沖突
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
for (int i = 0; i < m; i++) {
int a = sc.nextInt();
int b = sc.nextInt();
conflict[a][b] = conflict[b][a] = true;
}
// dp[i][j] 代表垄分,i個骰子且最頂面是j的情況種數(shù) 并且使用了滾動dp,否則會超空間
BigInteger dp[][] = new BigInteger[2][7];
int e = 0;
for (int i = 1; i < 7; i++)
dp[e][i] = BigInteger.ONE;
for (int i = 2; i <= n; i++) {
e = 1 - e;
for (int j = 1; j < 7; j++) {
dp[e][j] = BigInteger.ZERO;
for (int k = 1; k < 7; k++) {
if (!conflict[init[j]][k])
dp[e][j] = dp[e][j].add(dp[1 - e][k]).mod(
new BigInteger(MOD + ""));
System.out.println("dp["+e+"]["+j+"]="+dp[e][j]);
}
}
}
System.out.println("e="+e);
BigInteger sum = BigInteger.ZERO;
for (int i = 1; i < 7; i++) {
sum = sum.add(dp[e][i]).mod(new BigInteger(MOD + ""));
}
System.out.println("sum = "+sum);
System.out.println(sum.multiply(quickpow(4, n)).mod(
new BigInteger(MOD + "")));
}
// 快速冪
static BigInteger quickpow(int n, int m) {
BigInteger n1 = new BigInteger(n + "");
BigInteger t = BigInteger.ONE;
while (m > 0) {
if ((m & 1) == 1)
t = t.multiply(n1).mod(new BigInteger(MOD + ""));
n1 = n1.multiply(n1).mod(new BigInteger(MOD + ""));
m >>= 1;
}
return t;
}
}