Tyvj 两周年庆典要到了,Sam 想为 Tyvj 做一个大蛋糕。蛋糕俯视图是一个 N×M 的矩形,它被划分成 N×M 个边长为 1×1 的小正方形区域(可以把蛋糕当成 NNN 行 MMM 列的矩阵)。蛋糕很快做好了,但光秃秃的蛋糕肯定不好看!所以,Sam 要在蛋糕的上表面涂抹果酱。果酱有三种,分别是红果酱、绿果酱、蓝果酱,三种果酱的编号分别为 1,2,31,2,31,2,3。为了保证蛋糕的视觉效果,Admin 下达了死命令:相邻的区域严禁使用同种果酱。但 Sam 在接到这条命令之前,已经涂好了蛋糕第 KKK 行的果酱,且无法修改。
现在 Sam 想知道:能令 Admin 满意的涂果酱方案有多少种。请输出方案数 mod106。若不存在满足条件的方案,请输出 000。
样例输入(点击右上角复制按钮 即可复制)
2 2
1
2 3
样例输出
3
因为数据范围中很小,可以想到是状态压缩,而且毫无疑问地会选择压缩每一行。
因为这道题中有3种需要进行区分的果酱,所以使用三进制压缩,分别用0、1、2来表示。
然后按照普通的状压DP枚举每一行,进行合法方案数上的转移即可。
并且可以考虑预处理来优化:
1. 每一行总共有 种状态,但是因为相同颜色的果酱不能放在相邻的位置,所以每一行的不可行状态有很多。
e.g: m=3时,枚举了状态 111 或 000 或 222,显然,这样是没有必要的。
这就需要预处理出一行中的可行状态。经过统计,可以发现,即便是m=5,一行中的合法状态也只有48种,姑且当作50吧。
2. 在DP时,并不需要将每一种状态对应的十进制记录出来,更省空间且更简便的做法是:在储存时编号,这样就可以在遍历上一行和当前行的状态时直接取编号 ->这也是状压DP的常见套路了吧
3. 预处理行与行之间的判断
使得时间复杂度有很高的保证。
总体最高时间复杂度为 (也有粗略的四舍五入啊)大概250万的样子
细节问题也要注意啊:在运算过程中也有溢出的可能性
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define N 10006
#define mod 1000000
using namespace std;
inline int wread(){
char c=getchar ();int flag=1,wans=0;
while (c<'0'||c>'9'){if (c=='-') flag=-1;c=getchar ();}
while (c>='0'&&c<='9'){wans=wans*10+c-'0';c=getchar ();}
return wans*=flag;
}
int n,m;
int thre[250];//3^i的值
int lin_cal[5];//临时存储
int cal[50][5],top_cal;//每一种合法状态的每一位的数字 合法状态用编号储存
int pre[50];//取出合法的状态
int bel[250];
//三进制处理
void getthree(int x){
thre[0]=1;
for (int i=1;i<=x;++i)
thre[i]=thre[i-1]*3;
for (int i=0;i<thre[x];++i){//不能取等
int nx=i,top_wei=0;
while (nx) {
lin_cal[++top_wei]=nx%3;
nx/=3;
}
bool Jud=true;
for (int j=2;j<=x;++j)
if (lin_cal[j]==lin_cal[j-1]) {Jud=false;break;}
if (!Jud) continue;
++top_cal;
pre[top_cal]=i;
bel[i]=top_cal;
for (int j=1;j<=x;++j){
cal[top_cal][j]=lin_cal[j];
}
}
}
bool jud[50][50];
//预处理 判断
void getjud (){
for (int i=1;i<=top_cal;++i){
jud[i][i]=false;
for (int j=i+1;j<=top_cal;++j){
bool Jud=true;
for (int k=1;k<=m;++k){
if (cal[i][k]==cal[j][k]) {Jud=false;break;}
}
if (!Jud) jud[i][j]=jud[j][i]=false;
else jud[i][j]=jud[j][i]=true;
}
}
}
int made_l,l_zh;
int dp[N][50];
int main (){
n=wread();m=wread();
getthree(m);
getjud();
made_l=wread();
for (int i=1;i<=m;++i){
lin_cal[i]=wread();
lin_cal[i]--;
l_zh+=lin_cal[i]*thre[m-i];
}
if (!bel[l_zh]) {puts("0");return 0;}
if (made_l==1) {
dp[1][bel[l_zh]]=1;
for (int A=2;A<=n;++A)
for (int i=1;i<=top_cal;++i) //这一行状态
for (int j=1;j<=top_cal;++j) //上一行状态
if (jud[i][j]) dp[A][i]=(dp[A-1][j]%mod+dp[A][i]%mod)%mod;
int ans=0;
for (int i=1;i<=top_cal;++i)
ans=(ans%mod+dp[n][i]%mod)%mod;
printf("%d\n",ans);
}
else {
for (int i=1;i<=top_cal;++i)
dp[1][i]=1;
for (int A=2;A<=n;++A){
if (A==made_l){
for (int j=1;j<=top_cal;++j)
if (jud[bel[l_zh]][j]) dp[A][bel[l_zh]]=(dp[A-1][j]%mod+dp[A][bel[l_zh]]%mod)%mod;
continue;
}
for (int i=1;i<=top_cal;++i)//这一行的状态
for (int j=1;j<=top_cal;++j)//上一行的状态
if (jud[i][j]) dp[A][i]=(dp[A-1][j]%mod+dp[A][i]%mod)%mod;
}
int ans=0;
for (int i=1;i<=top_cal;++i)
ans=(ans%mod+dp[n][i]%mod)%mod;
printf("%d\n",ans);
}
return 0;
}