题目链接:https://loj.ac/problem/10172
题目描述
Tyvj 两周年庆典要到了,Sam 想为 Tyvj 做一个大蛋糕。蛋糕俯视图是一个 N×MN×MN×M 的矩形,它被划分成 N×MN×MN×M 个边长为 1×11×11×1 的小正方形区域(可以把蛋糕当成 NNN 行 MMM 列的矩阵)。蛋糕很快做好了,但光秃秃的蛋糕肯定不好看!所以,Sam 要在蛋糕的上表面涂抹果酱。果酱有三种,分别是红果酱、绿果酱、蓝果酱,三种果酱的编号分别为 1,2,31,2,31,2,3。为了保证蛋糕的视觉效果,Admin 下达了死命令:相邻的区域严禁使用同种果酱。但 Sam 在接到这条命令之前,已经涂好了蛋糕第 KKK 行的果酱,且无法修改。
现在 Sam 想知道:能令 Admin 满意的涂果酱方案有多少种。请输出方案数 mod 106\bmod 10^6mod106。若不存在满足条件的方案,请输出 000。
输入格式
输入共三行。
第一行:N,MN, MN,M;
第二行:KKK;
第三行:MMM 个整数,表示第 KKK 行的方案。
字母的详细含义见题目描述,其他参见样例。
输出格式
输出仅一行,为可行的方案总数。
样例
样例输入
2 2
1
2 3
样例输出
3
样例说明
方案一 | 方案二 | 方案三 |
---|---|---|
2 3\texttt{2 3}2 3 1 2\texttt{1 2}1 2 |
2 3\texttt{2 3}2 3 3 1\texttt{3 1}3 1 |
2 3\texttt{2 3}2 3 3 2\texttt{3 2}3 2 |
数据范围与提示
对于 30% 的数据,1≤N×M≤201≤N×M≤201≤N×M≤20;
对于 60% 的数据,1≤N≤1000,1≤M≤31≤N≤1000,1≤M≤31≤N≤1000,1≤M≤3;
对于 100% 的数据,1≤N≤10000,1≤M≤51≤N≤10000,1≤M≤51≤N≤10000,1≤M≤5。
题解
在网上找的题解,都不太清楚,让本蒟蒻卡了一下午加一晚上。
所以我弄懂以后,我就自己写了一篇,希望大家喜欢!
这道题是一道很好的状态压缩dp,因为有三种颜色,所以不能压成二进制,只能压成三进制。
当然啦,状压dp是基本一定要初始化的,别问我怎么知道的。
我们考虑以下两个初始化(就是代码里的pd1,pd2)
1. 每一行总共有很多种,但是因为相同颜色的果酱不能放在相邻的位置,所以每一行的不可行状态有很多。
这就需要预处理出一行中的可行状态。这样的话我们的dp数量会大大减少。
3. 预处理行与行之间的判断
跟预处理类似,减去不合法状态。
然后,就是我们的dp了。
其实这题很好想,dp[i][j]表示第i行的第j种状态,最后取个摸就行了。
方程:dp[i][j]+=dp[i-1][k]
最后的最后,大力推荐一下我的读入优化,跑的是真滴快!
#define num ch-'0'
inline void get(int &res)
{
char ch;bool flag=;
while(!isdigit(ch=getchar()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getchar());res=res*+num);
(flag)&&(res=-res);
}
代码如下:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define MAXN 10010
#define mod 1000000
#define num ch-'0'
using namespace std;
inline int MAX(int a,int b){return a>b?a:b;}
inline int MIN(int a,int b){return a<b?a:b;}
inline void get(int &res)
{
char ch;bool flag=;
while(!isdigit(ch=getchar()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getchar());res=res*+num);
(flag)&&(res=-res);
}
int n,g,m,k;
int dp[MAXN][],ok[][];
int a[],c[],t;
inline int pd1(int x)
{
for(register int i=m-;i;--i)if((x%c[i+])/c[i]==(x%c[i])/c[i-]) return ;
return ;
}
inline int pd2(int x,int y)
{
for(register int i=m-;i>=;--i) if((x%c[i+])/c[i]==(y%c[i+])/c[i]) return ;
return ;
}
int main()
{
get(n),get(m),get(k);
c[]=;
int num1=;
for(register int i=;i<=m;++i)
{
get(g);
num1=num1*+(g-);
c[i]=c[i-]*;
}
if(!pd1(num1)) {puts("");return ;}
for(register int i=;i<c[m];++i)
{
if(pd1(i)) a[++t]=i;
if(num1==i) dp[][t]=;
}
for(register int i=;i<=t;++i)
for(register int j=;j<=t;++j)
ok[i][j]=pd2(a[i],a[j]);
int res1=,res2=;
int x=MIN(n-k,k-),y=MAX(n-k,k-);
for(register int i=;i<=y;++i)
for(register int j=;j<=t;++j)
for(register int k=;k<=t;++k)
if(ok[k][j])
(dp[i][j]+=dp[i-][k])%=mod;
for(register int i=;i<=t;++i)
{
res1=(res1+dp[x][i])%mod;
res2=(res2+dp[y][i])%mod;
}
printf("%lld",((long long)res2*res1)%mod);
}