题目大意
给出 (n,m,k) ,表示有一个 (n imes m) 的网格,第 (k) 行已经确定了,每个格子只能填 (1,2,3),但是必须满足相邻格子颜色不同,问有多少种合法方案。
(1le kle nle 10^4,mle 5)
思路
因为这道题目并不难,所以这篇博客主要讲如何卡到 ( ext{LOJ rank1})(至少在2020-09-17还是,希望还有线性方法的出现吧,感觉非线性做法已经压到极致了)
很显然,我们可以设 (dp[i][S]) 表示第 (i) 行状态为 (S) 的方案数。初始显然,转移显然。下面讲优化。
- 滚动数组
经实测,可以优化 (20ms) 左右。
- 预处理
可以处理出哪些状态合法,已经每个状态可以从哪些状态转移过来。后者可以用 ( ext{vector}) 储存下来。
- 压缩
你发现合法的状态最多只有 (3 imes 2^{m-1}le48) 个,你可以先搜索一波,然后预处理的时候只考虑这 (48) 之间的关系就好了。
此时时间复杂度已经降为了 (Theta((3 imes 2^m)^2n)) 。
- 矩阵优化
你发现这个东西可以矩阵优化,显然,时间复杂度降为 (Theta(27 imes 8^m imes log n))
注意:矩阵乘法的时候先用 ( ext{long long}) 运算,这样可以减少取模次数
( exttt{Code})
#pragma GCC optimize("O3")
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define ll long long
#define mod 1000000
#define MAXN 6
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void Add (T &a,int b){a = a + b >= mod ? a + b - mod : a + b;}
vector <int> G[50];bool vis;
int n,m,k,tot,sta,ans,nows,s[5],ind[50],dp[2][50];
void dfs (int x,int las,int sta){
if (x == m) return ind[++ tot] = sta,vis |= (sta == nows),void ();
for (Int i = 1;i <= 3;++ i) if (i ^ las) dfs (x + 1,i,sta | (1 << x * 2) * i);
}
ll tmp[50][50];
struct Matrix{
int val[50][50];
Matrix(){memset (val,0,sizeof (val));}
int * operator [] (int key){return val[key];}
Matrix operator * (const Matrix &p)const{
Matrix New;
memset (tmp,0,sizeof (tmp));
for (Int i = 0;i <= tot;++ i)
for (Int j = 0;j <= tot;++ j)
for (Int k = 0;k <= tot;++ k)
tmp[i][j] += 1ll * val[i][k] * p.val[k][j];
for (Int i = 0;i <= tot;++ i)
for (Int j = 0;j <= tot;++ j)
New[i][j] = tmp[i][j] % mod;
return New;
}
}A,B,C;
Matrix operator ^ (Matrix a,int b){
Matrix res;
for (Int i = 0;i <= tot;++ i) res[i][i] = 1;
for (;b;b >>= 1,a = a * a) if (b & 1) res = res * a;
return res;
}
signed main(){
read (n,m,k);
for (Int i = 0;i < m;++ i) read (s[i]),nows |= ((1 << (i * 2)) * s[i]);
dfs (0,0,0);if (!vis) return puts ("0"),0;int bel;
for (Int i = 1;i <= tot;++ i){
if (ind[i] == nows) bel = i;
for (Int j = i + 1;j <= tot;++ j){
int S1 = ind[i],S2 = ind[j];bool flag = 1;
for (Int j = 0;j < m;++ j) if ((S1 >> (j * 2) & 3) == (S2 >> (j * 2) & 3)){flag = 0;break;}
if (flag) A[i][j] = A[j][i] = 1;
}
A[i][0] = 1;
}
B[bel][0] = 1;
for (Int i = 1;i <= tot;++ i) B[bel][i] = A[bel][i];
C = (A ^ (k - 1)) * B * (A ^ (n - k));
for (Int i = 1;i <= tot;++ i) Add (ans,C[i][0]);
write (ans),putchar ('
');
return 0;
}