P8624 [蓝桥杯 2015 省 AB] 垒骰子 题解
思路分析:
一、思考整体思路及状态表示式,以便后续根据递推关系建立状态转移方程。
又因为每个骰子侧面可以旋转,即每个骰子可能性
因而当不考虑互斥情况时,状态转移方程应为
同时最终答案应为
二、考虑互斥情况以及
oppo 的处理方式。
但这题需要考虑互斥情况,接下来考虑互斥的处理。
利用
因为是考虑紧挨之间是否存在互斥关系。
如考虑第
因而引入
不考虑互斥情况时状态转移方程:
考虑互斥情况后状态转移方程:
通过图像理解也很直观:
另外对于
Ⅰ、数组保存:利用
int oppo[7]={0,4,5,6,1,2,3};
Ⅱ、函数计算:
int get_oppo(int x){
if(x>3) return x-3;
else return x+3;
}
三、进一步思考
对于答案,
即
以及每一个
转化成伪代码:
for(int i=1;i<=6;i++) f[1][i]=4;
for(int i=2;i<=n;i++)
for(int j=1;j<=6;j++)
if(非互斥) f[i][j] = (f[i][j] + f[i - 1][j] * 4)
易知时间复杂度为
考虑到数据范围:
显然会超时,需要进行优化!
四、矩阵加速(矩阵快速幂)
考虑到数据范围:
使用矩阵快速幂的方式,复杂度
观察可发现,
将
for(int i=1;i<=6;i++)res.c[1][i]=4;
对于常数矩阵
for(int i=1;i<=6;i++){
for(int j=1;j<=6;j++){
if(st[i][oppo[j]]) A.c[i][j]=0;
else A.c[i][j]=4;
}
}
最终代码:
#include<bits/stdc++.h>
using namespace std;
#define rep(x,y,z) for(int x=y;x<=z;x++)
typedef long long LL;
const int mod=1e9+7;
int n,m,a,b,oppo[7]={0,4,5,6,1,2,3};
bool st[7][7];
struct matrix{
LL c[7][7];
matrix(){memset(c,0,sizeof c);}
}A,res;
matrix operator * (matrix &x,matrix &y){
matrix t;
rep(i,1,6){
rep(j,1,6){
rep(k,1,6){
t.c[i][j]=(t.c[i][j]+x.c[i][k]*y.c[k][j])%mod;
}
}
}
return t;
}
void fastpow(LL k){
rep(i,1,6) res.c[1][i]=4;
rep(i,1,6){
rep(j,1,6){
if(st[i][oppo[j]]) A.c[i][j]=0;
else A.c[i][j]=4;
}
}
while(k){
if(k&1) res=res*A;
A=A*A;
k>>=1;
}
}
int main(){
cin>>n>>m;
while(m--){
cin>>a>>b;
st[a][b]=st[b][a]=1;
}
fastpow(n-1);
LL ans=0;
rep(i,1,6) ans=(ans%mod+res.c[1][i]%mod)%mod;
cout<<ans;
return 0;
}