JZOJ5442. 荒诞
题目大意
给出一个\(n\)个点, \(m\)条边的无向联通图, 满足任意两点间不存在节点数超过\(10\)的简单路径, 现在要在一些节点放旅游站, 第\(i\)个节点放旅游站的代价为\(a_i\), 使得图中的每一个点满足 : 这个点放了旅游站或与这个点直接相连的点至少有一个放了旅游站. 求最小总代价.
\(n <= 2*10^4, m <= 2.5*10^4\).
对于\(15\%\)的数据, 满足原图是一棵树.
解题思路
下面我们定义: 一个点被"影响"意味与这个点直接相连的点至少有一个放了旅游站.
先考虑对于树的情况怎么做: 是一个经典的树形dp. 设\(f_{x, 0/1/2}\), 其中\(0\)表示这个点没有被影响且没有放旅游站, \(1\)表示这个点放了旅游站, \(2\)表示这个点被影响了且没有放旅游站. 转移显然.
现在拓展到图上. 注意到这张图有一个奇怪的性质: 任意两点间不存在节点数超过\(10\)的简单路径. 也就是说这个图建出\(\text{dfs}\)树后深度不超过\(10\). 考虑用这个性质搞点大东西.
建出\(\text{dfs}\)树, 显然这棵树上没有横叉边只有返祖边, 于是不同子树之间不会相互影响, 一个点能影响的只有自己的子树和这个点到根的一条链.
前者显然可以通过递归时自上而下传递dp值来处理, 而对于后者. 因为后者的长度\(<=10\), 所以可以状压记录状态.
在dp的时候注意: 由于我们需要合并儿子节点的信息, 但是.. 并不好合并, 所以可以考虑一个一个在回溯时更新父节点的状态, 以此影响对于其它儿子的计算.
这种dp的顺序, 如果写下来就是欧拉序, 于是成功学习了欧拉序上的dp.
一些细节要想清楚. 以及空间要滚动. 理论上时间复杂度是\(O(3^{10}*10n)\), 但是由于时限三秒且无用状态数很多所以不需要信仰也能过.
后记
考场的树部分分打炸了.... 这种问题还是没能避免, 一定要好好拍..
#include <cstdio>
#include <cstring>
#define L 10
#define N 20010
#define M 25010
#define INF 0x3f3f3f3f
#define init(a, b) memset(a, b, sizeof(a))
#define fo(i, a, b) for(int i = (a); i <= (b); ++i)
#define fd(i, a, b) for(int i = (a); i >= (b); --i)
#define fe(i, u) for(int i = last[u], v = to[last[u]]; i; v = to[i = pre[i]])
using namespace std;
inline int read()
{
int x = 0; char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return x;
}
bool vis[N];
int n, m, last[N], pre[M << 1], to[M << 1];
int a[N], dep[N], fa[N], p3[L + 5] = {1}, f[2][59050];
inline int min(int a, int b){return a < b ? a : b;}
inline void upd(int &a, int b){a = min(a, b);}
inline void add(int u, int v){static int tot = 0; to[++tot] = v, pre[tot] = last[u], last[u] = tot;}
void dfs(int u)
{
dep[u] = dep[fa[u]] + 1; vis[u] = 1;
fe(i, u) if(!vis[v]) fa[v] = u, dfs(v);
}
// 0: no station no influenced
// 1: one station here
// 2: no station but influenced
inline int getw(int s, int w){return s / p3[w] % 3;}
void dp(int u)
{
bool cur = dep[u] & 1, con[L + 5];
init(con, 0);
fo(i, 0, p3[dep[u]]) f[cur][i] = INF;
fe(i, u) if(dep[v] < dep[u]) con[dep[v] - 1] = 1;
fo(s, 0, p3[dep[u] - 1] - 1)
{
if(f[cur ^ 1][s] == INF) continue ;
int s1 = s + p3[dep[u] - 1]; bool flu = 0;
fo(i, 0, dep[u] - 2) if(con[i])
!getw(s, i) && (s1 += (p3[i] << 1)), flu |= (getw(s, i) == 1);
f[cur][s1] = min(f[cur][s1], f[cur ^ 1][s] + a[u]);
f[cur][s + flu * p3[dep[u] - 1] * 2] = f[cur ^ 1][s];
}
fe(i, u) if(fa[v] == u)
{
dp(v);
fo(s, 0, p3[dep[u]] - 1)
f[cur][s] = min(f[cur ^ 1][s + p3[dep[v] - 1]], f[cur ^ 1][s + (p3[dep[v] - 1] << 1)]);
}
}
int main()
{
freopen("absurdity.in", "r", stdin);
freopen("absurdity.out", "w", stdout);
fo(i, 1, L) p3[i] = p3[i - 1] * 3;
n = read(), m = read(); int u, v, ans = 0;
fo(i, 1, n) a[i] = read();
fo(i, 1, m) u = read(), v = read(), add(u, v), add(v, u);
fo(i, 1, n) if(!vis[i]) dfs(i), f[0][0] = 0, dp(i), ans += min(f[1][1], f[1][2]);
printf("%d", ans);
return 0;
}
浙公网安备 33010602011771号