Tyvj 两周年庆典要到了,Sam 想为 Tyvj 做一个大蛋糕。蛋糕俯视图是一个 N×M 的矩形,它被划分成 N×M个边长为 1×1 的小正方形区域(可以把蛋糕当成 N行M列的矩阵)。蛋糕很快做好了,但光秃秃的蛋糕肯定不好看!所以,Sam 要在蛋糕的上表面涂抹果酱。果酱有三种,分别是红果酱、绿果酱、蓝果酱,三种果酱的编号分别为 1,2,3。为了保证蛋糕的视觉效果,Admin 下达了死命令:相邻的区域严禁使用同种果酱。但 Sam 在接到这条命令之前,已经涂好了蛋糕第 K 行的果酱,且无法修改。
现在 Sam 想知道:能令 Admin 满意的涂果酱方案有多少种。请输出方案数 mod 10^6。若不存在满足条件的方案,请输出 0。
输入共三行。
第一行:N,M;
第二行:K;
第三行:M 个整数,表示第 K 行的方案。
字母的详细含义见题目描述,其他参见样例。
输出仅一行,为可行的方案总数。
2 2
1
2 3
3
方案一 | 方案二 | 方案三 |
---|---|---|
2 3 | 2 3 | 2 3 |
1 2 | 3 1 | 3 2 |
对于 30% 的数据,1≤N×M≤20;
对于 60% 的数据,1≤N≤1000,1≤M≤3;
对于 100% 的数据,1≤N≤10000,1≤M≤5。
本题的数据透露了本题的做法——状态压缩dp。但一般的状压dp都是通过二进制数来记录每个单元的0-1的状态(即能不能,选不选等),但本题涉及到三个3种状态,显然无法通过传统的二进制位运算进行状态压缩。故而,我们想到将其转化为一个类似于三进制的数的形式来压缩状态。
#include
using namespace std;
#define int long long
int state[300],dp[10003][300],s1,g;
int n,m,k,cnt=0,s2;
int const mod=1e6;
inline bool check(int a,int b){//判断两个状态能否作为相邻的两行
if(a==b)return 0;
while(a){
if(a%10==b%10)return 0;
a/=10;b/=10;
}
return 1;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++)g=g*10+3;//预处理最大情况
cin>>k;
for(int i=1;i<=m;++i){
int x;
cin>>x;
if(s1%10==x)s2=1;//判断的k行的状态是否合法
s1=s1*10+x;
}
if(s2){
cout<<0;
return 0;
}
for(int i=pow(10,m-1);i<=g;i++){//寻找所有可能合法的状态,即相邻两数都不相等的状态
int flag=1,x=i,p;
while(x){
p=x%10;x=x/10;
if(p==x%10)flag=0;
if(p==0||p>3)flag=0;
}
if(flag)state[++cnt]=i;
if(i==s1)s2=cnt;//找到第k行状态所对应的编号
}
if(k==1)dp[1][s2]=1;
else{
for(int i=1;i<=cnt;i++)
dp[1][i]=1;
}
for(int i=2;i<=n;i++){
if(i!=k){
for(int k1=1;k1<=cnt;k1++){//枚举前一行的状态
for(int k2=1;k2<=cnt;k2++){//枚举当前行的状态
if(check(state[k1],state[k2]))
dp[i][k2]=(dp[i][k2]+dp[i-1][k1])%mod;
}
}
}
else{
for(int k=1;k<=cnt;k++){
if(check(s1,state[k]))
dp[i][s2]=(dp[i-1][k]+dp[i][s2])%mod;
}
}
}
int ans=0;
for(int i=1;i<=cnt;i++){
ans=(ans+dp[n][i])%mod;
}
cout<