题目链接
一道三进制状压的好题。
题目描述:
Tyvj 两周年庆典要到了,Sam 想为 Tyvj 做一个大蛋糕。蛋糕俯视图是一个 N×M的矩形,它被划分成 N×M个边长为 1×1的小正方形区域(可以把蛋糕当成 N 行 M 列的矩阵)。
蛋糕很快做好了,但光秃秃的蛋糕肯定不好看!
所以,Sam 要在蛋糕的上表面涂抹果酱。果酱有三种,分别是红果酱、绿果酱、蓝果酱,
三种果酱的编号分别为 1,2,3.为了保证蛋糕的视觉效果,Admin 下达了死命令:
相邻的区域严禁使用同种果酱。但 Sam 在接到这条命令之前,
已经涂好了蛋糕第 KKK 行的果酱,且无法修改。 现在 Sam 想知道:能令 Admin 满意的涂果酱方案有多少种。请输出方案数 mod1e6。若不存在满足条件的方案,请输出 0。
输入格式
输入共三行。
第一行:N,M;
第二行:K;
第三行:M 个整数,表示第 K 行的方案。
字母的详细含义见题目描述,其他参见样例。
输出格式
输出仅一行,为可行的方案总数。
样例
样例输入
2 2 1 2 3
样例输出
3
解题思路:
这道题关键在判断合法情况,第k行特判一下即可。
1.判断一个三进制数是否有相同数字相邻的情况,不能模拟二进制左移右移的情况,
因为这里有3个数字,左移右移会出现有0的影响。
2.判断不同行是否有相同数字相邻,模拟二进制的&判断一下即可。
代码:
#include<bits/stdc++.h> #define ll long long #define R register using namespace std; int n,m,k,mod=1e6,a[250],sk,num,top,ans,f[10005][250]; inline int ksm(R int x,R int p) {R int tot=1;while(p){if(p&1){tot=tot*x;}x=x*x;p>>=1;}return tot; } inline int check(R int x,R int y) {for(R int i=1;i<=m;++i){if((x%3)==(y%3))return 0;x/=3;y/=3;}return 1; } inline int judge(R int x) {R int y=-1;for(R int i=1;i<=m;++i){ if(y==x%3)return 0;y=x%3;x/=3;}return 1; } inline void init() {for(R int i=0;i<=242;++i){R int x=i,tot=0;while(x){x/=3;++tot;}if(tot>=m+1)break;if(judge(i)){ a[++num]=i; if(i==sk)top=num;}} } int main(){scanf("%d%d",&n,&m);scanf("%d",&k);for(R int i=1;i<=m;++i){R int t;scanf("%d",&t);sk+=(t-1)*ksm(3,i-1);}if(!judge(sk)) {printf("0");return 0;}init();if(k==1)f[1][top]=1;elsefor(R int i=1;i<=num;++i)f[1][i]=1;for(R int i=2;i<=n;++i)//当前第几行 {if(i==k){for(R int t=1;t<=num;++t)if(check(a[top],a[t]))f[i][top]=(f[i][top]+f[i-1][t])%mod;}else{for(R int j=1;j<=num;++j)//当前行状态 {if(i-1==k){if(check(a[j],a[top]))f[i][j]=(f[i][j]+f[i-1][top])%mod;}else{for(R int t=1;t<=num;++t)//上一行状态if(check(a[j],a[t]))f[i][j]=(f[i][j]+f[i-1][t])%mod;}}}}for(R int i=1;i<=num;++i)ans=(ans+f[n][i])%mod;printf("%d",ans%mod);return 0; }
这道题关键在于舍弃不合法情况的判断.