Address
https://www.lydsy.com/JudgeOnline/problem.php?id=1494
Solution
定义状态:
f[i][S]
f
[
i
]
[
S
]
表示
i
i
个点的图,编号差 的点对之间有连边,现选出一些边,连通第
1
1
个点到第 个点,第
i−k+1
i
−
k
+
1
个点到第
i
i
个点的连通性为 ,第
i−k+1
i
−
k
+
1
个点到第
i
i
个点至少存在一点与前 个点连通,且选出的边无环的方案数。。
注意这里的
S
S
压缩连通性使用的是最小表示法:
对连通块进行标号,从 到连通块个数,首次出现位置越靠前的连通块编号越小。
会发现有效
S
S
的个数是贝尔数,即第二类斯特林数的前缀和。 时状态数只有
52
52
。
如何求
f[i+1][T]
f
[
i
+
1
]
[
T
]
从
f[i][S]
f
[
i
]
[
S
]
的转移系数呢?????
我们可以对于一个
S
S
, 枚举第
i−k+1
i
−
k
+
1
个点到第
i
i
个点是否与 连边,大力计算即可。
但要注意:
(1)如果第
i−k+1
i
−
k
+
1
个点在第
i−k+1
i
−
k
+
1
到第
i
i
个点中单独属于一个连通块,那么 和
i+1
i
+
1
间必须连边,否则不能保证「连通前
i−k+1
i
−
k
+
1
个点」。
(2)不能由
i
i
连向第 个点到第
i
i
<script type="math/tex" id="MathJax-Element-5003">i</script> 个点中的同一个连通块,否则会形成环。
发现转移系数不变且为一阶。
因此直接上矩阵快速幂。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
typedef long long ll;
const int N = 60, E = 7, ZZQ = 65521;
int k, m, tmp[E], pmt[E], col[N][E], vis[E], pwe[E][E];
ll n;
struct cyx {
int n, m, a[N][N];
cyx() {}
cyx(int _n, int _m) :
n(_n), m(_m) {memset(a, 0, sizeof(a));}
friend inline cyx operator * (cyx a, cyx b) {
int i, j, k;
cyx res = cyx(a.n, b.m);
For (i, 1, res.n) For (j, 1, res.m) For (k, 1, a.m)
res.a[i][j] = (res.a[i][j] +
1ll * a.a[i][k] * b.a[k][j] % ZZQ) % ZZQ;
return res;
}
friend inline cyx operator ^ (cyx a, ll b) {
int i;
cyx res = cyx(a.n, a.m);
For (i, 1, a.n) res.a[i][i] = 1;
while (b) {
if (b & 1) res = res * a;
a = a * a;
b >>= 1;
}
return res;
}
} S, A;
void orzcyx(int dep, int cnt) {
int i;
if (dep == k + 1) {
col[++m][0] = 1;
For (i, 1, k) col[m][i] = tmp[i];
For (i, 1, k) pmt[i] = 0;
For (i, 1, k) pmt[tmp[i]]++;
For (i, 1, k) if (pmt[i] > 1)
col[m][0] *= pwe[pmt[i]][pmt[i] - 2];
return;
}
For (i, 1, cnt) tmp[dep] = i, orzcyx(dep + 1, cnt);
tmp[dep] = ++cnt; orzcyx(dep + 1, cnt);
}
int trans(int st, int qn) {
int i, j, tot = 0; bool o = 0;
For (i, 2, k) if (col[st][i] == col[st][1])
{o = 1; break;}
if (!o && !(qn & 1)) return -1;
For (i, 1, k) For (j, i + 1, k)
if (((qn >> i - 1) & 1) && ((qn >> j - 1) & 1)
&& col[st][i] == col[st][j]) return -1;
For (i, 1, k) tmp[i] = col[st][i];
For (i, 1, k) if ((qn >> i - 1) & 1)
For (j, 1, k) if (tmp[j] == col[st][i]) tmp[j] = k + 1;
For (i, 1, k - 1) tmp[i] = tmp[i + 1];
tmp[k] = k + 1;
For (i, 1, k + 1) vis[i] = 0;
For (i, 1, k) {
if (!vis[tmp[i]]) vis[tmp[i]] = ++tot;
pmt[i] = vis[tmp[i]];
}
For (i, 1, m) {
bool is = 1;
For (j, 1, k)
if (col[i][j] != pmt[j]) {is = 0; break;}
if (is) return i;
}
return -1;
}
int main() {
int i, j;
cin >> k >> n;
For (i, 1, k) {
pwe[i][0] = 1;
For (j, 1, k) pwe[i][j] = pwe[i][j - 1] * i;
}
orzcyx(1, 0);
S = cyx(m, 1); A = cyx(m, m);
For (i, 1, m) For (j, 0, (1 << k) - 1) {
int z = trans(i, j);
if (z != -1) A.a[z][i]++;
}
For (i, 1, m) S.a[i][1] = col[i][0];
cout << ((A ^ n - k) * S).a[1][1] << endl;
return 0;
}