[蓝桥杯2015初赛]垒骰子
题目大意:
给你n个骰子,给你m组不能贴在一起的面,问你把这n个骰子垒成高度为n的柱体,一共有多少种方案?
题目分析:
- 首先,不能贴近的面是肯定不能在一起的,我们只需要每次放骰子时不把不能贴近的面放一块儿就行,也就是每个顶面,都可以再放上与它不冲突的面,最后顶面一共会出现6中情况,我们对顶面的6种情况进行dp。
- dp[i][j]代表的就是高度为i时,顶面为j的方案数,可知
dp[i][j]=i-1层中所有与j的反面不冲突的面相加,不管i-1层的顶面时什么,只要不与j的反面相冲突即可加入。 - 由于n的大小有1e9,所以这道题必定要在时间复杂度优化上下功夫,这里可以看出,给你的每个面的反面都是确定的,且冲突的面都是确定的,那么状态转移是不是线性的呢?是线性的,每次都做的是一样的操作,所以我们采用矩阵快速幂优化dp
- 不了解矩阵快速幂的同学可以先去学学
代码如下
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
int n,m;
int t[7]={0,4,5,6,1,2,3};
struct node {
ll v[7][7];
}first;
void init(){
for(int i=1;i<7;i++)
for(int j=1;j<7;j++)
first.v[i][j]=1;
}
node mul(node x,node y){
node t;
for(int i=1;i<7;i++)
for(int j=1;j<7;j++)
{
t.v[i][j]=0;
for(int k=1;k<7;k++){
t.v[i][j]+=(x.v[i][k]*y.v[k][j]%mod);
t.v[i][j]%=mod;
}
}
return t;
}
node mul1(node x,node y)
{
node t;
for(int i=1;i<=1;i++)
for(int j=1;j<7;j++)
{
t.v[i][j]=0;
for(int k=1;k<7;k++)
{
t.v[i][j]+=(x.v[i][k]*y.v[k][j]%mod);
t.v[i][j]%=mod;
}
}
return t;
}
node quick_mi(int k){
node tem;
for(int i=1;i<7;i++)
for(int j=1;j<7;j++){
if(i==j)
tem.v[i][j]=1;
else
tem.v[i][j]=0;
}
while(k){
if(k&1){
tem=mul(tem,first);
}
first=mul(first,first);
k/=2;
}
return tem;
}
ll quick_mi1(ll x,int n)
{
ll tot=1;
while(n)
{
if(n&1)
tot=tot*x%mod;
x=x*x%mod;
n>>=1;
}
return tot;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
while(cin>>n>>m){
init();
while(m--){
int a,b;
cin>>a>>b;
first.v[t[a]][b]=0;
first.v[t[b]][a]=0;
}
node book;
ll sum=0;
for(int i=1;i<7;i++)
book.v[1][i]=1;
node ans=quick_mi(n-1);
ans=mul1(book,ans);
for(int i=1;i<7;i++)
sum=(sum+ans.v[1][i])%mod;
ll x=quick_mi1(4,n);
sum=sum*x%mod;
cout<<sum<<endl;
}
return 0;
}
有问题的话,可以在下方评论哦