[FROM WOJ]#3880 涂抹果酱

#3880 涂抹果酱

鬼知道为什么我卡了那么久

题面
yvj 两周年庆典要到了,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
1 2
方案二
2 3
3 1
方案三
2 3
3 2
对于 30% 的数据, 1 ≤ N × M ≤ 20 1≤N×M≤20 1N×M20
对于 60% 的数据, 1 ≤ N ≤ 1000 , 1 ≤ M ≤ 3 1≤N≤1000,1≤M≤3 1N1000,1M3
对于 100% 的数据, 1 ≤ N ≤ 10000 , 1 ≤ M ≤ 5 1≤N≤10000,1≤M≤5 1N10000,1M5

SOL
读完题后可以得知,每个格子只看有三种状态,即三种颜色,于是将题目转成三进制来做就好。
三进制的比较并不是很难,逐个比较每一位就好了(只是函数写法不要太皮)。
其实这道题最关键的是如何处理第k行。
思考后可以发现,将网格划分为k行以上、k行一下的2个部分,然后k位首行初始化,最后通过乘法原理处理一下就好了。
此外,这道题容易超时,因此我建议所有状态都预处理。
注意细节。

代码:

#include<bits/stdc++.h>
#define int long long
#define N 10005
#define M 250
using namespace std;
inline int rd(){
	int register data=0;static char ch=0;
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))data=(data<<1)+(data<<3)+ch-'0',ch=getchar();
	return data;
}

inline void write(int x){if(x>9)write(x/10);putchar(x%10+'0');}
int n,m,k,f[N][M],s[M],ok[M][M],c[6],cnt;
const int mod=1e6;
inline int und(int x,int y){for(int register i=m-1;i>=0;--i) if((x%c[i+1])/c[i]==(y%c[i+1])/c[i]) return 0;return 1;}
inline int umd(int x){for(int register i=m-1;i;--i)if((x%c[i+1])/c[i]==(x%c[i])/c[i-1]) return 0;return 1;}
signed main(){
	n=rd();m=rd();k=rd();c[0]=1;for(int register a=1;a<=m;a++)c[a]=c[a-1]*3;
	int register tmp=0;
	for(int register i=1;i<=m;i++){tmp=tmp*3+rd()-1;}
	if(!umd(tmp)){putchar('0');return 0;}
	for(int register i=0;i<c[m];i++){
		if(!umd(i))continue;
		s[++cnt]=i;
		if(i==tmp)f[0][cnt]=1;
	}
	for(int register i=1;i<=cnt;i++)for(int register j=1;j<=cnt;j++)ok[i][j]=und(s[i],s[j]);
	int register mn=min(n-k,k-1),mx=max(n-k,k-1);
	for(int register i=1;i<=mx;i++){
		for(int register j=1;j<=cnt;j++){
			if(!f[i-1][j])continue;
			for(int register k=1;k<=cnt;k++){
				if(!ok[k][j])continue;
				f[i][k]=(f[i][k]+f[i-1][j])%mod;
			}
		}		
	}
	int register ans1=0,ans2=0;
	for(int register i=1;i<=cnt;i++)ans1=(ans1+f[mn][i])%mod,ans2=(ans2+f[mx][i]%mod);
	write(ans1*ans2%mod);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值