题解:AT_arc057_d [ARC057D] 全域木
思路
转化题意,问你在一个带权无向完全图中,如何填上
考虑模仿 kruskal 的方式,令
这样只能通过
考虑优化。
注意到我们其实不需要按照边来存储状态,我们只需要知道连通块的大小即可,考虑构造一种状态集合
这样我们对于每条树边才需要转移,因为非树边不会对状态有影响。
你发现状态的连通块个数顶多只有
总结一下,我们令
当然具体实现中使用刷表法。
解释一下,对于树边,我们枚举两个不同的连通块处理;对于非树边,我们只需要预处理出每种状态的
实现
框架
首先你至少要把状态预处理出来,然后就按照树边和非树边干就完了。
代码
#include <bits/stdc++.h>
const int MAXM = 41 * 41;
const int MAXP = 37400;
const int MOD = 1e9 + 7;
int add(int a, int b) { return (a + b >= MOD) ? a + b - MOD : a + b; }
int dec(int a, int b) { return (a - b < 0) ? a - b + MOD : a - b; }
int mul(int a, int b) { return (1LL * a * b) % MOD; }
void pluson(int &a, int b) {a = add(a, b);}
int n;
bool V[MAXM];
std::map<std::vector<int>, int> S;
std::map<int, std::vector<int>> Sback;
std::vector<int> nowS, Snext;
int id = 0;
int C[MAXP];
void dfs(int x, int v, int aim) {
if (x == 0 && v == aim) {
S[nowS] = ++id, Sback[id] = nowS;
for(int i = 0; i < nowS.size(); i++) C[id] += nowS[i] * (nowS[i] - 1) / 2;
return ;
}
if (x >= v && v != 0) {
int len = nowS.size();
for (int i = 0; i < len; i++) nowS[i]++;
dfs(x - v, v, aim);
for (int i = 0; i < len; i++) nowS[i]--;
}
if (x >= 1) {
nowS.push_back(1);
dfs(x - 1, v + 1, aim);
nowS.pop_back();
}
}
int m;
int f[MAXM][MAXP];
void solve()
{
f[0][id] = 1;
for (int i = 1; i <= m; i++)
for (int j = 1; j <= id; j++)
if (f[i - 1][j]) {
if (V[i]) {
nowS = Sback[j];
for (int a = 0; a < nowS.size(); a++)
for (int b = a + 1; b < nowS.size(); b++) {
Snext.clear();
Snext.push_back(nowS[a] + nowS[b]);
for (int k = 0; k < nowS.size(); k++)
if (k != a && k != b)
Snext.push_back(nowS[k]);
std::sort(Snext.begin(), Snext.end());
std::reverse(Snext.begin(), Snext.end());
pluson(f[i][S[Snext]], mul(f[i - 1][j], mul(nowS[a], nowS[b])));
}
}
else
pluson(f[i][j], mul(f[i - 1][j], C[j] - i + 1));
}
}
int main()
{
scanf("%d", &n);
for (int i = 1; i < n; i++) {
int read; scanf("%d", &read);
V[read] = true;
}
m = n * (n - 1) / 2;
/*初始化状态*/
for (int i = 1; i <= n; i++)
dfs(n, 0, i);
solve();
int Ans = 0;
for (int i = 1; i <= id; i++)
pluson(Ans, f[m][i]);
printf("%d", Ans % MOD);
return 0;
}
总结
善于模仿算法的实现方式。
优化状态,首先找到算法的本质,然后只存储需要用到的性质,本题中利用组合数学简化了一些状态,具体的,连边这一类问题,通过连通块的组合数即可求出。