#轮廓线dp#洛谷 2435 染色

题目

有一个 \(n\)\(m\) 列的格点图,你需要给每个点上染上 \(k\) 种颜色中的一种,

要求没有两个相邻点颜色相同。给定第一行与最后一行的染色,试求总染色方案数。


分析

首先对于 \(k=2\) 要特判,因为存在 \(m\) 特别大的情况,

否则可以将每种颜色压成二位二进制,直接状压会TLE,

\(dp[n][m][S]\) 表示处理到 \((n,m)\) 轮廓线颜色状态为 \(S\)的方案数,

还是分第一个格子和其它格子判断,注意 \(k=3\) 时颜色 3 不能选择。


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#define rr register
using namespace std;
const int N=65536,mod=376544743; bool bbc[N<<1];
int n,m,k,two[16],f[N],dp[N],al,S;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline signed mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
signed main(){
	n=iut(),m=iut(),k=iut();
	if (k==2){
		for (rr int i=0;i<m;++i) bbc[i]=iut();
		for (rr int i=0;i<m;++i)
		    if (bbc[i]^iut()^(n&1)^1) return !putchar(48);
		return !putchar(49);
    }
    two[0]=1;
    for (rr int i=1;i<2*m;++i)
	    two[i]=two[i-1]<<1;
    for (rr int i=0;i<m;++i){
    	rr int x=iut();
    	if (x&1) S|=two[i<<1];
    	if (x&2) S|=two[i<<1|1]; 
	}
    dp[S]=1,al=1<<(m*2);
    for (rr int i=1;i<n;++i){
    	memset(f,0,sizeof(int)*al);
    	for (rr int j=0;j<al;++j){
    		if ((j&3)==3&&k==3) continue;
    		for (rr int t=0;t<k;++t)
    		if ((j&3)!=t)
    		    f[j]=mo(f[j],dp[j^(j&3)^t]);
		}
    	memcpy(dp,f,sizeof(int)*al);
    	for (rr int o=1;o<m;++o){
    		memset(f,0,sizeof(int)*al);
    		for (rr int j=0;j<al;++j){
    			rr int F=j&(two[o*2-2]|two[o*2-1]);
    			rr int now=j&(two[o<<1]|two[o<<1|1]);
    			if (now==F*4) continue;
    			if (now==(two[o<<1]|two[o<<1|1])&&k==3) continue;
    			for (rr int t=0;t<k;++t)
    			if (now!=t*two[o<<1])
    			    f[j]=mo(f[j],dp[j^now^(t*two[o<<1])]);
			}
    		memcpy(dp,f,sizeof(int)*al);
		}
	}
	S=0;
    for (rr int i=0;i<m;++i){
    	rr int x=iut();
    	if (x&1) S|=two[i<<1];
    	if (x&2) S|=two[i<<1|1]; 
	}
	return !printf("%d",dp[S]);
}
posted @ 2021-08-25 08:50  lemondinosaur  阅读(51)  评论(0编辑  收藏  举报