Live2D

题解 「一本通 5.4 练习 1」涂抹果酱

题目传送门

题目大意

给出 \(n,m,k\) ,表示有一个 \(n\times m\) 的网格,第 \(k\) 行已经确定了,每个格子只能填 \(1,2,3\),但是必须满足相邻格子颜色不同,问有多少种合法方案。

\(1\le k\le n\le 10^4,m\le 5\)

思路

因为这道题目并不难,所以这篇博客主要讲如何卡到 \(\text{LOJ rank1}\)(至少在2020-09-17还是,希望还有线性方法的出现吧,感觉非线性做法已经压到极致了)

很显然,我们可以设 \(dp[i][S]\) 表示第 \(i\) 行状态为 \(S\) 的方案数。初始显然,转移显然。下面讲优化。

  • 滚动数组

经实测,可以优化 \(20ms\) 左右。

  • 预处理

可以处理出哪些状态合法,已经每个状态可以从哪些状态转移过来。后者可以用 \(\text{vector}\) 储存下来。

  • 压缩

你发现合法的状态最多只有 \(3\times 2^{m-1}\le48\) 个,你可以先搜索一波,然后预处理的时候只考虑这 \(48\) 之间的关系就好了。

此时时间复杂度已经降为了 \(\Theta((3\times 2^m)^2n)\)

  • 矩阵优化

你发现这个东西可以矩阵优化,显然,时间复杂度降为 \(\Theta(27\times 8^m\times \log n)\)


注意:矩阵乘法的时候先用 \(\text{long long}\) 运算,这样可以减少取模次数

\(\texttt{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 ('\n');
	return 0; 
}
posted @ 2020-09-17 14:50  Dark_Romance  阅读(156)  评论(0编辑  收藏  举报