[ZJOI2016]小星星(容斥+dp)
题目:洛谷P3349、LOJ#2091
题目描述:
给一棵\(n\)个点的树以及一个\(n\)个点,\(m\)条边的图,问有多少种排列\(p\)使得对于任意\(i,j\)如果\(p_{i}\)和\(p_{j}\)在树上有边,那么\(i\)和\(j\)在图上一定也有边
\(n \leq 17\),\(m \leq \frac{n \cdot (n-1)}{2}\)
蒟蒻题解:
容易想到暴力dp,设\(f[i][j][k]\)表示以\(i\)为根,\(i\)对应图上的点\(j\),且\(i\)子树内的点对应图上的点集为\(k\),加入\(i\)的一个儿子\(u\):
\[f[i][j][k] = \sum_{t \subseteq s \subseteq k}(f[i][j][s] \cdot f[u][t][k \bigotimes s])
\]
但是这样复杂度是\(\Theta(n^{3} \cdot 3^{n})\)的,会\(T\)飞
尝试优化掉第三维\(dp\),如果把第三维去掉会出现树上多个点对应图上相同的节点的情况,还要减去重复的情况
考虑容斥,先枚举一个集合\(S\),然后树上每个点都要属于集合上的点,设\(f[i][j]\)表示以\(i\)为根,\(i\)对应图上的点\(j\)的方案数,则加入\(i\)的一个儿子\(u\):
\[f[i][j] = \sum_{k = 1}^{k \leq n}(f[i][j] \cdot f[u][k])
\]
最终的答案为:
\[ans=\sum_{|S|=n}f[1][i]-\sum_{|S|=n-1}f[1][i]+\sum_{|S|=n-2}f[1][i]-...\pm\sum_{|S|=1}f[1][i]
\]
时间复杂度为:\(\Theta(n^{3} \cdot 2^{n})\)
参考程序:
#include<bits/stdc++.h>
using namespace std;
#define Re register int
typedef long long ll;
const int M = 280;
int n, m, cnt, hea[20], nxt[M], to[M];
ll s, ans, g[20], f[20][20];
bool p[20], b[20][20];
inline int read()
{
char c = getchar();
int ans = 0;
while (c < 48 || c > 57) c = getchar();
while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
return ans;
}
inline void add(int x, int y)
{
nxt[++cnt] = hea[x], to[cnt] = y, hea[x] = cnt;
}
inline void dfs(int x, int fa)
{
for (Re i = 1; i <= n; ++i) f[x][i] = p[i];
for (Re i = hea[x]; i; i = nxt[i])
{
int u = to[i];
if (u == fa) continue;
dfs(u, x);
for (Re j = 1; j <= n; ++j)
if (p[j])
{
ll s = 0;
for (Re k = 1; k <= n; ++k)
if (p[k] && b[j][k]) s += f[u][k];
f[x][j] *= s;
}
}
}
inline void calc(int x)
{
dfs(1, 0);
s = 0;
for (Re i = 1; i <= n; ++i)
if (p[i]) s += f[1][i];
((n - x) & 1) ? ans -= s : ans += s;
}
inline void sea(int x, int y)
{
if (x == n + 1)
{
calc(y);
return;
}
p[x] = 0, sea(x + 1, y);
p[x] = 1, sea(x + 1, y + 1);
}
int main()
{
n = read(), m = read();
for (Re i = 0; i < m; ++i)
{
int u = read(), v = read();
b[u][v] = b[v][u] = 1;
}
for (Re i = 1; i < n; ++i)
{
int u = read(), v = read();
add(u, v), add(v, u);
}
sea(1, 0);
printf("%lld", ans);
return 0;
}

浙公网安备 33010602011771号