【ZJOI 2016 小星星】【容斥 + 树形 DP】

来源:互联网 发布:缓存服务器软件 编辑:程序博客网 时间:2024/06/04 18:32

容斥好题喵呜~

我们一共要满足两个限制:

  1. 树中的一个点对应图中一个点,且一一对应。
  2. 树中两点有边的,图中两点也对应有边。

O(nn) 我们可以直接暴力枚举每个点的对应关系,判断是否可以每个树中的点都对应到图中,如果满足,ans++

优化一些的暴力是用 f[i][j] 表示 i 为根的子树,使用了 j 所表示的点映射到图上的方案数。
第一次 dfs,预处理出 pre[i][j]=1 表示树中的 i 可以对应到图中的 j 中。
第二次 dfs,暴力合并。
枚举当前点 u 的初始状态(即 pre[u][x]=1 的所有 x)的子集 S,从 (以 u 为根的子树的子集) 转移到 (S 合并上 以 u 为根的子树的子集)。
形如:f[u][i|j]+=f[v][j]i 是 子集 Sj 为以 u 为根的子树的子集。
复杂度 O(3nn2),考虑优化。如果你有高深的卡常技巧好像也是可以过的。

如果我们放宽限制,只统计满足限制 2 的方案数,那么每个点映射的点构成的集合是可重集 S,也就是说树上两个不同点的映射可以相同,那么我们显然可以用树形 DP O(N3) 转移。
f[i][j] 表示树上 i 号点映射图中 j 号点的方案数,每次枚举根对应一个节点,再枚举每个儿子节点对应节点 f[k][l],乘起来就可以了,可以做到 O(n3) 计算。注意 k 的枚举一共只有 n 次。

但是我们无法满足限制 1 一一对应,那实际上不合法的状态可以被用两种方法表示:至少有一个点匹配了多个点;至少有一个点没有被匹配到。
这样的话就可以发现强制某些点不选就可以构造出需要去掉的不合法情况,方案数 = 所有点都可以匹配到 - 至少有1个点未被选 + 至少有2个点未被选 - 至少有3个点未被选 …(这里的图上的点可以被树上的点重复覆盖) ,即若至少有偶数个点为被选,就加上,否则减去。
那么对于 S,我们记 sum=Σf[root][j],jS,如果 n|S| 是偶数 ans 就加上,否则减去。(容斥)

我们将这个模型抽象出来:

A(i) 表示包含了原图上点 i 的映射集合,则答案集合为 A(1)A(2)A(3)A(n) 的交集,因为 |A(1)A(2)A(n)|=|A(1)A(2)A(n)||A(2)A(n)|+,所以我们枚举 2n1 个并集,用树上 DP 计算出并集的大小,容斥一下就能得到 |A(1)A(2)A(n)| 了。
—— By wzj 大爷

总复杂度 O(2nn3)

复习一下:枚举子集的二进制写法

// 枚举 i 的子集for (int j = i & (i - 1); j; j = i & (j - 1))

这样做就是每次不断 - 1 来枚举所有子集,它不是忽略了 i 中的 0,而是在 & i 的过程中将 0 消去了。

理解了这个枚举子集抽离点的部分就很容易了。

#include <bits/stdc++.h>#define ll long longusing namespace std;const int N = 25;struct Edge {    int next, to;   }e[N << 1];ll tot = 0, sum = 0, ans = 0;ll a[N], mapp[N][N], f[N][N];int cnt = 0, head[N];void add(int u, int v) {    e[++ cnt].to = v; e[cnt].next = head[u]; head[u] = cnt;}void dfs(int u, int fa) {    for (int i = head[u]; i; i = e[i].next) {        int v = e[i].to;        if (v == fa) continue;        dfs(v, u);    }    for (int i = 1; i <= tot; i ++) {        f[u][i] = 1;        for (int j = head[u]; j; j = e[j].next) {            int v = e[j].to;            if (v == fa) continue;            ll tmp = 0;            for (int k = 1; k <= tot; k ++)                if (mapp[a[i]][a[k]]) tmp += f[v][k];            f[u][i] *= tmp;        }    }}int main() {    int n, m;    scanf("%d%d", &n, &m);    for (int i = 1; i <= m; i ++) {        int u, v;        scanf("%d%d", &u, &v);        mapp[u][v] = 1, mapp[v][u] = 1;     }    for (int i = 1; i < n; i ++) {        int u, v;        scanf("%d%d", &u, &v);        add(u, v), add(v, u);    }    for (int i = 1; i <= (1 << n) - 1; i ++) {        tot = 0, sum = 0;        for (int j = 1; j <= n; j ++) if (i & (1 << (j - 1))) a[++ tot] = j; // a[] 中存的就是枚举出来的状态 i 所包含的点        dfs(1, 0);        for (int j = 1; j <= tot; j ++) sum += f[1][j];        ans += (ll)((n - tot) & 1) ? -sum : sum; // 容斥    }    printf("%lld\n", ans);    return 0;   }
阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 锦夜简介 锦夜全文免费阅读 博物奇妙夜方锦龙 夜神润玉强锦觅 锦觅夜神睡了几次 锦夜txt下载 锦觅和夜神双休过吗 锦夜1全文免费阅读 锦夜 小狮 锦夜2 锦夜2全文阅读 锦娘 锦字组词 锦字拼音 锦字起名 锦字成语 锦字 分类字锦 锦字取名男孩 带锦字的女孩名字 带锦字的成语 锦字起名女孩名字 锦字在名字中的寓意 带有锦字的大气男孩名字 锦字开头的成语 锦字多少笔画 带锦字的男孩名字 锦字辈女孩洋气名字 字字锦 锦宅玲珑秀 锦宅 锦食记宅包 平遥锦宅酒店 锦宅 玲珑秀 锦食记 宅包 平遥锦宅 锦宏高考 锦宏 锦宫春 安娜锦之宫 锦宫标签机色带