题意:
给定一个
4
∗
n
4*n
4∗n的矩阵,可以用任意面积为
4
4
4的连通图形覆盖
完美覆盖的方案个数,对
998244353
998244353
998244353取模,
n
≤
1
e
9
n\le 1e9
n≤1e9
首先会想到状压
d
p
dp
dp,因为最坏情况就是一个横着的长为 4 的插到前面
这样的话需要压 3 列的状态
然后容易想到先把状态的转移打表打出来
然后你发现讨论所有 4 联通块已经让你头疼了,还要枚举转移
我们无脑直接搜索 4 联通块的形态,为了不重复计算,强制当前连通块顶着最后一列
并且强制当前填的在已经填了的下面
直接枚举 16 个格子的状态判断合不合法即可
这就处理可以填进去的东西
然后处理转移矩阵,先枚举前 3 列,然后最后一列强制必须选
得到后两列的情况后枚举它的超集,用刚刚预处理的可以填进去的东西去转移
然后你发现如果直接做的复杂度是
O
(
n
∗
2
24
)
O(n*2^{24})
O(n∗224)
接着你发现可以矩阵乘法,紧接着你想到应该可以线性递推
然后发现要解出转移矩阵的特征多项式非常的不可做
我们考虑到,如果解出了它的特征多项式,然后求出
A
n
=
∑
i
=
0
k
b
i
A
i
A^n=\sum_{i=0}^kb_iA^i
An=∑i=0kbiAi,那么最后的答案就是
∑
i
=
0
k
b
i
∗
v
∗
A
i
=
∑
i
=
0
k
b
i
∗
d
p
i
\sum_{i=0}^kb_i*v*A^i=\sum_{i=0}^kb_i*dp_i
∑i=0kbi∗v∗Ai=∑i=0kbi∗dpi
有一个性质是如果知道
A
n
=
∑
i
=
0
k
b
i
A
i
A^n=\sum_{i=0}^kb_iA^i
An=∑i=0kbiAi,那么
A
n
+
1
=
∑
i
=
0
k
b
i
A
i
+
1
A^{n+1}=\sum_{i=0}^kb_iA^{i+1}
An+1=∑i=0kbiAi+1
并且他们的转移系数是一样的,所以矩阵
A
A
A 可以线性递推
那么矩阵中的某一个值,在这里是第
2
12
−
1
2^{12}-1
212−1行
2
12
−
1
2^{12}-1
212−1 列,也是可以线性递推的
于是暴力预处理前 200 项用
B
M
BM
BM 解出最短递推式,发现长度是 35
直接把表拿到你的标程里面线性递推即可
复杂度
O
(
T
l
o
g
(
n
)
3
5
2
)
O(Tlog(n)35^2)
O(Tlog(n)352)
收获挺多的
现场学了一波
B
M
BM
BM,觉得增量构造特别巧妙
然后学到了不用手算而暴力搜索的
t
r
i
c
k
trick
trick
然后还有为了避免重复计数钦定顺序的方法
复习了一下线性递推,还了解了
A
i
A^i
Ai 可以线性递推的巧妙性质
#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int Mod = 998244353;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int ksm(int a, int b){ int ans = 1; for(;b;b>>=1, a=mul(a,a)) if(b&1) ans=mul(ans, a); return ans; }
int popcnt(int S){ int x = 0; while(S) S -= S&-S, ++x; return x; }
int cntzero(int S){
for(int i = 0; i < 4; i++) if((1 << i) & S) return i;
return 4;
}
void Add(int &a, int b){ a += b; if(a >= Mod) a -= Mod; }
namespace ckbit{
int vis[4][4], cur = 0;
int d[4][2] = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}};
int dfs(int x, int y, int S){
vis[x][y] = cur;
int res = 1;
for(int k = 0; k < 4; k++){
int nx = x + d[k][0], ny = y + d[k][1];
if(nx >= 0 && ny >= 0 && nx < 4 && ny < 4 && (S >> (nx + ny * 4) & 1) && vis[nx][ny] != cur) res += dfs(nx, ny, S);
} return res;
}
bool ck(int S){
int res = 0;
for(int x = 0; x < 4; x++){
if(S >> (x + 3 * 4) & 1){
++cur; res = dfs(x, 3, S);
break;
}
} return res == popcnt(S);
}
} using namespace ckbit;
cs int SIZE = 300;
int a[SIZE];
#define poly vector<int>
poly operator + (poly a, poly b){
poly c; int len = max(a.size(), b.size());
a.resize(len); b.resize(len); c.resize(len);
for(int i = 0; i < len; i++) c[i] = add(a[i], b[i]);
return c;
}
namespace BM{
poly R[SIZE];
int cur = 0, fail[SIZE], delta[SIZE];
void upt(int n){
int t = a[n];
for(int i = 1; i < R[cur].size(); i++) t = add(t, Mod - mul(R[cur][i], a[n - i]));
delta[n] = t;
if(!t) return;
fail[cur] = n;
if(!cur){ R[++cur].resize(n + 1); return; }
t = mul(t, ksm(delta[fail[cur - 1]], Mod - 2));
R[cur + 1].resize(n - fail[cur - 1]);
R[cur + 1].push_back(t);
for(int i = 1; i < R[cur-1].size(); i++) R[cur + 1].push_back(mul(Mod-R[cur-1][i], t));
R[cur + 1] = R[cur] + R[cur + 1];
cur++;
}
void solve(){
cout << R[cur].size() << endl;
for(int i = 1; i < R[cur].size(); i++) cout << R[cur][i] << ",";
puts("");
for(int i = 1; i < R[cur].size(); i++) cout << a[i] << ",";
puts("");
}
}
void init(){
static int blk[1 << 16 | 5], cnt = 0;
for(int S = 1; S < (1 << 16); S++) if(popcnt(S) == 4 && ck(S)) blk[++cnt] = S;
static int bs[1 << 16 | 5];
bs[0] = 1;
for(int S = 0; S < (1 << 16); S++)
for(int i = 1; i <= cnt; i++){
if(bs[S] && !(S & blk[i]) && ((S >> 12) == 0 || cntzero(S >> 12) > cntzero(blk[i] >> 12)))
Add(bs[S | blk[i]], bs[S]);
}
static int trans[1 << 12 | 5][1 << 12 | 5];
static int id[1 << 13 | 5]; int ct = 0;
static bool vis[1 << 12];
for(int S = 0; S < (1 << 12); S++){
for(int T = S >> 4; T < 1 << 12; T = (T + 1) | (S >> 4)){
int T0 = (T << 4) | ((1 << 4) - 1);
if(bs[T0 ^ S]) trans[S][T] = bs[T0 ^ S];
}
}
cs int K = 100, U = (1 << 12) - 1;
static int dp[(K << 1) + 5][U + 5];
dp[0][U] = 1;
for(int i = 0; i < (K << 1); i++) for(int S = 0; S < (1 << 12); S++)
if(dp[i][S]){ for(int T = 0; T < (1 << 12); T++){
dp[i + 1][T] = add(dp[i + 1][T], mul(dp[i][S], trans[S][T]));
}
}
for(int i = 0; i <= (K << 1); i++) a[i] = dp[i][U], BM::upt(i);
puts("");
BM::solve();
}
int main(){ init(); }
偷懒写的 n 2 n^2 n2 取模
#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 40, K = 35;
int a[N] = {1,1,4,23,117,454,2003,9157,40899,179399,796558,3546996,15747348,69834517,310058192,378623792,
122781000,179638924,664040578,693857604,150068224,487780789,81153444,201496361,206356061,443239188,
923221963,678050018,834687149,938086801,37535712,164288625,780488339,49747373,890150065,992668567};
int r[N] = {0,2,8,8,54,998244276,998244063,998244277,998243805,469,2258,414,1970,
998243300,998237468,998242733,998241004,1102,9566,3210,2786,998243864,998238306,
998241753,998243251,60,1476,659,225,39,998244230,998244303,998244340,998244350,3,1};
#define poly vector<int>
int T, n;
cs int Mod = 998244353;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int ksm(int a, int b){ int ans=1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans, a); return ans; }
poly mod, pw[31];
poly operator * (poly a, poly b){
int deg = a.size() + b.size() - 1;
poly c; c.resize(deg);
for(int i = 0; i < a.size(); i++)
for(int j = 0; j < b.size(); j++)
c[i + j] = add(c[i + j], mul(a[i], b[j]));
for(int i = deg - 1; i >= K; i--){
if(c[i]){
int inv = c[i];
for(int j = 0; j <= K; j++){
c[i - j] = add(c[i - j], Mod - mul(mod[K - j], inv));
}
}
} if(deg > K) c.resize(K); return c;
}
int calc(int n){
if(n <= K) return a[n];
poly nx; nx.push_back(1);
for(int i = 0; i <= 30; i++) if(n>>i&1) nx = nx * pw[i];
int ans = 0;
for(int i = 0; i < K; i++) ans = add(ans, mul(a[i], nx[i]));
return ans;
}
int main(){
scanf("%d", &T);
mod.resize(K + 1);
for(int i = 1; i <= K; i++) mod[K - i] = r[i] ? Mod - r[i] : 0; mod[K] = 1;
pw[0].push_back(0); pw[0].push_back(1);
for(int i = 1; i < 30; i++){
pw[i] = pw[i-1] * pw[i-1];
}
while(T--){ scanf("%d", &n); cout << calc(n) << '\n';}
return 0;
}