【T1】 朝夕相处
【思路要点】
- 记不考虑旋转同构的答案为 G ( N ) G(N) G(N),答案为 F ( N ) F(N) F(N),由 B u r n s i d e Burnside Burnside引理,有 F ( N ) = ∑ i ∣ N G ( i ) φ ( N i ) N F(N)=\frac{\sum_{i\mid N}G(i)\varphi(\frac{N}{i})}{N} F(N)=N∑i∣NG(i)φ(iN)
- 剩余问题在于计算 G ( N ) G(N) G(N) ,考虑状态 ( i , C o l a , C o l b , D i s t a , D i s t b ) (i,Cola,Colb,Dista,Distb) (i,Cola,Colb,Dista,Distb) ,表示完成左端点在 i i i 之前的所有瓷砖的放置与染色后,第一行最后一个瓷砖颜色为 C o l a Cola Cola ,位置为 i + D i s t a i+Dista i+Dista ,第二行最后一个瓷砖颜色为 C o l b Colb Colb ,位置为 i + D i s t b i+Distb i+Distb 。枚举左端点在 0 0 0 处时的状态,计算转移到 N N N 处的相同状态的方案数,对于所有状态求和,即为 G ( N ) G(N) G(N) 。
- 使用矩阵乘法或 B e r l e k a m p − M a s s e y Berlekamp-Massey Berlekamp−Massey 算法可以对该动态规划进行优化。
- 状态总数为 O ( K 2 ) O(K^2) O(K2),若采用 B e r l e k a m p − M a s s e y Berlekamp-Massey Berlekamp−Massey 算法优化动态规划,总时间复杂度为 O ( M K 6 L o g K + M 2 N L o g N ) O(MK^6LogK+M^2\sqrt{N}LogN) O(MK6LogK+M2NLogN) ,其中 M M M 为递推阶数,有 M = 31 M=31 M=31 。
- 用最小表示法表示一个状态的颜色可将状态总数降至 O ( M ) O(M) O(M),总时间复杂度降为 O ( M 4 L o g M + M 2 N L o g N ) O(M^4LogM+M^2\sqrt{N}LogN) O(M4LogM+M2NLogN)
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 105; const int P = 1e9 + 7; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) { x = max(x, y); } template <typename T> void chkmin(T &x, T y) { x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int power(int x, int y) { if (y == 0) return 1; int tmp = power(x, y / 2); if (y % 2 == 0) return 1ll * tmp * tmp % P; else return 1ll * tmp * tmp % P * x % P; } void update(int &x, int y) { x += y; if (x >= P) x -= P; } namespace LinearSequence { const int MAXN = 5005; const int MAXLOG = 62; const int P = 1e9 + 7; vector <int> a[MAXN]; int cnt, delta[MAXN], fail[MAXN]; int k, h[MAXN], now[MAXLOG][MAXN]; int power(int x, int y) { if (y == 0) return 1; int tmp = power(x, y / 2); if (y % 2 == 0) return 1ll * tmp * tmp % P; else return 1ll * tmp * tmp % P * x % P; } void times(int *res, int *x, int *y) { static int tmp[MAXN]; memset(tmp, 0, sizeof(tmp)); for (int i = 0; i <= k - 1; i++) for (int j = 0; j <= k - 1; j++) tmp[i + j] = (tmp[i + j] + 1ll * x[i] * y[j]) % P; for (int i = 2 * k - 2; i >= k; i--) { int val = tmp[i]; tmp[i] = 0; for (unsigned j = 0; j < a[cnt].size(); j++) tmp[i - j - 1] = (tmp[i - j - 1] + 1ll * val * a[cnt][j]) % P; } memcpy(res, tmp, sizeof(tmp)); } void init(int n, int *val) { for (int i = 0; i <= cnt; i++) a[i].clear(); cnt = 0; for (int i = 1; i <= n; i++) { delta[i] = val[i]; for (unsigned j = 0; j < a[cnt].size(); j++) delta[i] = (delta[i] - 1ll * a[cnt][j] * val[i - j - 1] % P + P) % P; if (delta[i] == 0) continue; fail[cnt] = i; if (cnt == 0) { a[++cnt].resize(i); continue; } int mul = 1ll * delta[i] * power(delta[fail[cnt - 1]], P - 2) % P; a[cnt + 1].resize(i - fail[cnt - 1] - 1); a[cnt + 1].push_back(mul); for (unsigned j = 0; j < a[cnt - 1].size(); j++) a[cnt + 1].push_back(1ll * a[cnt - 1][j] * (P - mul) % P); if (a[cnt + 1].size() < a[cnt].size()) a[cnt + 1].resize(a[cnt].size()); for (unsigned j = 0; j < a[cnt].size(); j++) a[cnt + 1][j] = (a[cnt + 1][j] + a[cnt][j]) % P; cnt++; } assert(n >= a[cnt].size() * 2 + 5); memset(now, 0, sizeof(now)); k = a[cnt].size(); if (k == 1) now[0][0] = a[cnt][0]; else now[0][1] = 1; for (int i = 1; i <= 2 * k; i++) h[i] = val[i]; for (int p = 1; p < MAXLOG; p++) times(now[p], now[p - 1], now[p - 1]); } int query(long long n) { if (n <= k) return h[n]; n -= k; static int res[MAXN]; memset(res, 0, sizeof(res)); res[0] = 1; for (int p = 0; p < MAXLOG; p++) { long long tmp = 1ll << p; if (n & tmp) times(res, res, now[p]); } int ans = 0; for (int i = 0; i <= k - 1; i++) ans = (ans + 1ll * res[i] * h[i + k]) % P; return ans; } } struct State { int cola, colb, dista, distb; }; bool operator < (State a, State b) { if (a.cola == b.cola) { if (a.colb == b.colb) { if (a.dista == b.dista) return a.distb < b.distb; else return a.dista < b.dista; } return a.colb < b.colb; } return a.cola < b.cola; } int n, m, ans[MAXN]; set <pair <State, int>> start; map <State, int> mp[MAXN]; void initstates() { State now = (State) { 0, 0, 0, 0}, res = now; res.cola = res.colb = 1; start.emplace(res, m); for (int dj = 1; dj <= 3; dj++) for (int dk = 1; dk <= 3; dk++) { State res = now; res.dista = dj, res.distb = dk