题目
题目描述
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 行的方案。
字母的详细含义见题目描述,其他参见样例。
输出格式
输出仅一行,为可行的方案总数。
样例
输入样例
1 2 3 4 |
2 2 1 2 3 |
输出样例
1 2 |
3 |
样例说明
数据范围和提示
对于 30% 的数据,1≤N×M≤20;
对于 60% 的数据,1≤N≤1000,1≤M≤3;
对于 100% 的数据,1≤N≤10000,1≤M≤5。
题解
一开始拿到这道题目发现不就是状态压缩嘛,而且是相邻不重复的计数问题,这种题目早就已经轻车熟路了。
但是仔细看了才发现不简单,不是普通的二进制状压,而是三进制状压。
但是进制本质上都是一样的,我们一边参照二进制状压,一边实现三进制状压。
首先是左移右移,实际上二进制的左移右移就是\(\times 2\)还有/2,既然是三进制,当然是*3还有/3
还有就是实现状态合法判定,具体的在check函数和judge函数的注释里,这两个函数稍微有点不同。
状态转移的话就是上一行的所有合法状态之和(这个和二进制状压是一样的)
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#include<iostream> #include<cstdio> using namespace std; const int MAXN=10004,mod=1000000; int n,m,k,pos; int ban,ans=0; int can[1005],tot=0; int f[MAXN][1005]; inline bool check(int x){//自身合法性判断,每次弹出最末尾的一个数,存起来,看看和下一个数是不是一样 int tmp=0x3f; for(int i=1;i<=m;++i){ if(tmp==x%3)return false; tmp=x%3,x/=3; } return true; } inline bool judge(int a,int b){//相互合法性判断,就是按位比对 for(int i=1;i<=m;++i){ if(a%3==b%3)return false; a/=3,b/=3; } return true; } int main(){ cin>>n>>m>>k; int in=1; for(int i=0;i<m;i++)in*=3; for(int i=0;i<in;++i)if(check(i))can[++tot]=i; for(int i=1;i<=m;++i){ int tmp; scanf("%d",&tmp); ban=ban*3+tmp-1; } for(int i=1;i<=tot;++i)if(ban==can[i]){pos=i;break;} if(!pos){cout<<0;return 0;} for(int i=1;i<=n;++i){ if(i==k){ if(i==1)f[i][pos]=1; else for(int j=1;j<=tot;++j)if(judge(can[pos],can[j]))(f[i][pos]+=f[i-1][j])%=mod; } else for(int j=1;j<=tot;++j){ if(i==1)f[i][j]=1; else for(int k=1;k<=tot;++k)if(judge(can[j],can[k]))(f[i][j]+=f[i-1][k])%=mod; } } for(int i=1;i<=tot;++i)(ans+=f[n][i])%=mod; cout<<ans; return 0; } |