神仙的容斥题与神仙的树形DP题。

首先搞一个指数级的做法:求总的、能够覆盖每一条边的方案数,通过容斥可以得到\(\text{ans}=\sum\limits_E{(-1)^{|E|}F(E)}\)。其中,\(F(E)\)表示钦定删除边集\(E\)后,其他的连边方案数。显然经过删边操作,这张图被划分成了很多联通块,联通块之间没有连边,方案数就是每个联通快的方案数的成绩。特别地,当\(|E|=0\)时,这个是总情况数。

如何求解一个联通块内的连边方案数呢?先上式子:\(\text{ret}=1 \times 3 \times 5 \times \cdots \times (n - 3) \times (n - 1)\)。如何理解这个式子呢?首先这个“联通块”并不是一定要求联通,因为我容斥的时候只要求某个边集断开,并没有要求其他的都联通呀!所以,随便找第一个点,它可以和\(n-1\)个点匹配;然后找下一个点,它可以和\(n-3\)个点匹配······最后总方案树就是它们的乘积。

现在我们有了指数级做法了(就是枚举\(E\),时间复杂度\(\Theta(n^2)\)),现在来尝试把它优化到多项式时间复杂度。

一个“联通块”的情况数,仅仅与“点数”的点数有关,这启示我们在 DP 中存下一维状态来存当前点集的大小;每条边都只有选和不选两种情况,这让我们联想到了 0/1 背包。

设 \(f_{u,i,0/1}\) 表示,\(u\)这个子树,跟\(u\)结点连着的“联通块”大小是\(i\),此时还未乘上\(i\)这个联通块的情况数的总情况数(也就是把这个子树删到只剩下\(i\)个点,不考虑\(i\)内部情况的总情况数)。显然这时\(f_{u,i,0/1}\)再乘上\(g[i]\)就是总情况数(\(g[i]=1 \times 3 \times \cdots \times (i - 3) \times (n - 1)\))。

转移的时候,考虑怎么合并把\(v\)合并到\(father_v\)上面;我们的目的是优化\(\Theta(n^2)\)枚举边集,所以看这条边钦定删掉和不钦定删掉两种情况,方程分别是:

\[f_{v,j,a} * f_{father,i,b} \rightarrow f^{'}_{father,i+j,a\oplus b}
\]
\[f_{v,j,a} * f_{father,i,b} * g[j] \rightarrow f^{'}_{father,i,a\oplus b\oplus 1}
\]

上面那个方程的含义是,不钦定删除 <v,fat> 那条边,那么它们下面的总情况数是它们的乘积;下面那个方程的意思是,不选这条边,那么下面的方案数要乘上\(v,b\)的总方案数,即\(f \times g\)。

最终的答案就等于\(\sum\limits_{i=1}^n{f_{1,i,0} - f_{1, i, 1}}\)。

总结一下,这里有几个重要的点:

1.转化为容斥的问题。

2.结合情况数只与结点数有关和边的出现与否只有两种情况,设计类似于 0/1 背包的 DP 方法。

3.树形 DP 不一定是考虑怎么从子节点们一次性推出父亲,还可以考虑怎么把子节点依次并进父亲的答案里面,这种 DP 的实现,需要把 DP 数组的其中一维拷贝一遍,具体实现看下面:

#include <bits/stdc++.h>
#define LL long long using namespace std;
const int maxn = 5e3 + 1e2;
const int mod = 1e9 + 7; int n, f[maxn][maxn][2], g[maxn], sze[maxn]; vector<int> T[maxn]; void dfs(int x, int fa)
{
int tmp[maxn][2];
memset(tmp, 0, sizeof tmp);
//cout << "HH" << endl;
//cout << x << endl;
sze[x] = 1;
f[x][1][0] = 1;
for (auto s : T[x])
{
if (s == fa) continue;
dfs(s, x);
for (int i = sze[x]; i; i--)
{
for (int j = sze[s]; j; j--)
{
for (int a = 0; a < 2; a++)
{
for (int b = 0; b < 2; b++)
{
(tmp[i + j][a ^ b] += (LL)f[x][i][a] * f[s][j][b] % mod) %= mod;
(tmp[i][a ^ b ^ 1] += (LL)f[x][i][a] * f[s][j][b] % mod * g[j] % mod) %= mod;
}
}
}
}
sze[x] += sze[s];
for (int i = 0; i <= sze[x]; i++)
{
for (int j = 0; j < 2; j++)
{
f[x][i][j] = tmp[i][j];
tmp[i][j] = 0;
}
}
}
} int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
T[u].push_back(v);
T[v].push_back(u);
}
g[0] = 1;
for (int i = 2; i <= n; i += 2)
{
g[i] = (LL)g[i - 2] * (i - 1) % mod;
}
dfs(1, 0);
int ans = 0;
for (int i = 1; i <= n; i++)
{
(ans += ((LL)f[1][i][0] - f[1][i][1] + mod) * g[i] % mod) %= mod;
}
cout << ans << endl; return 0;
}

[ARC101C] Ribbons on Tree的更多相关文章

  1. ARC101E - Ribbons on Tree

    题目链接 ARC101E - Ribbons on Tree 题解 令边集\(S \subseteq E\) 设\(f(S)\)为边集S中没有边被染色的方案数 容斥一下,那么\(ans = \sum_ ...

  2. Atcoder ARC101 Ribbons on Tree

    题解: 前面牛客网的那个比赛也有一道容斥+dp 两道感觉都挺不错的 比较容易想到的是 f[i][j]表示枚举到了i点,子树中有j个未匹配 这样的话我们需要枚举儿子中匹配状态 这样是n^2的(这是个经典 ...

  3. ARC 101E.Ribbons on Tree(容斥 DP 树形背包)

    题目链接 \(Description\) 给定一棵\(n\)个点的树.将这\(n\)个点两两配对,并对每一对点的最短路径染色.求有多少种配对方案使得所有边都至少被染色一次. \(n\leq5000\) ...

  4. [ARC101E]Ribbons on Tree(容斥,dp)

    Description 给定一棵有 \(n\) 个节点的树,满足 \(n\) 为偶数.初始时,每条边都为白色. 现在请你将这些点两两配对成 \(\frac{n}{2}\) 个无序点对.每个点对之间的的 ...

  5. ARC101E Ribbons on Tree 容斥原理+dp

    题目链接 https://atcoder.jp/contests/arc101/tasks/arc101_c 题解 直接容斥.题目要求每一条边都被覆盖,那么我们就容斥至少有几条边没有被覆盖. 那么没有 ...

  6. [atARC101E]Ribbons on Tree

    令$f(E')$表示强制$E'$中的边不被覆盖的方案数,根据容斥,$ans=\sum_{E'\subseteq E}(-1)^{|E'|}f(E')$ 对于给定的$E'$,$f(E')$即将$E'$中 ...

  7. Solution -「ARC 101E」「AT 4352」Ribbons on Tree

    \(\mathcal{Description}\)   Link.   给定一棵 \(n\) 个点的树,其中 \(2|n\),你需要把这些点两两配对,并把每对点间的路径染色.求使得所有边被染色的方案数 ...

  8. 【arc101】比赛记录

    这场还好切出了D,rt应该能涨,然而这场的题有点毒瘤,700分的D没多少人切,更别说EF了.(暴打出题人)既然这样,干脆就水一篇博客,做个简单的比赛记录. C - Candles 这题是一道一眼题,花 ...

  9. Atcoder 乱做

    最近感觉自己思维僵化,啥都不会做了-- ARC103 F Distance Sums 题意 给定第 \(i\) 个点到所有点的距离和 \(D_i\) ,要求构造一棵合法的树.满足第 \(i\) 个点到 ...

随机推荐

  1. 大闸蟹的OO第二单元总结

    OO的第二单元是讲多线程的协作与控制,三次作业分别为FAFS电梯,ALS电梯和三部需要协作的电梯.三次作业由浅入深,让我们逐渐理解多线程的工作原理和运行状况. 第一次作业: 第一次作业是傻瓜电梯,也就 ...

  2. jzoj6094

    题目描述 给定一个循环流(每个点均满足流量平衡条件),这个循环流有$n$个点,且每条边的流量只有 $1$ 或$ 2$,其中$a$条边流量为$1$,$b$条边流量为$2$,判断是否存在一个流满足上述条件 ...

  3. STM32直流电机启动(一)驱动电路的介绍

    驱动电路 典型的H桥驱动电路如下:要使电机旋转只需导通对角线上的两个三极管即可,如导通Q1,Q4,关闭Q2,Q4即可驱动电机正转:若想电机反向转动,即导通三极管Q2,Q3,关闭Q1,Q4.此时电路图可 ...

  4. f(sinx)到底是啥

    总结一句:cosx是偶次就一定可以用.

  5. best-time-to-buy-and-sell-stock leetcode C++

    Say you have an array for which the i th element is the price of a given stock on day i. If you were ...

  6. Java RMI学习与解读(三)

    Java RMI学习与解读(三) 写在前面 接下来这篇就是最感兴趣的Attack RMI部分了. 前面也说过,RMI的通信过程会用到反序列化,那么针对于RMI的三个角色: Server/Regisrt ...

  7. 挂载iscsi存储

    参考连接:https://segmentfault.com/a/1190000005853387?utm_source=tag-newest 安装客户端工具,iscsi-initiator yum i ...

  8. Spring一套全通4—持久层整合

    百知教育 - Spring系列课程 - 持久层整合 第一章.持久层整合 1.Spring框架为什么要与持久层技术进行整合 1. JavaEE开发需要持久层进行数据库的访问操作. 2. JDBC Hib ...

  9. Linux 用户&用户组

    用户和用户组的概念 用户 ---> 使用操作系统的人 Linux系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系 ...

  10. C++ 指针的引用和指向引用的指针

    指向引用的指针 简单使用指针的一个例子就是: int a = 1; int *p = &a; 预先强调: 没有指向引用的指针 原因: 因为引用 不是对象,没有地址. 但是指向引用的指针是什么形 ...