分析:比较难的一道题,看到要求方案数,又是在一棵树上,自然就想起了树形dp.状态该怎么表示呢?首先肯定有一维状态表示以i为根的子树,考虑到i有没有匹配对答案也是有影响的,自然而然状态就出来了:f[i][0/1]表示以i为根的子树中,i取或不取的最大匹配.因为要求方案数,再开一个数组g[i][0/1]记录方案数.

接下来考虑怎么转移.如果i不选,那么它的子节点不管选不选都没关系:

f[i][0] = Σmax{f[j][0],f[j][1]},如果i要选,那么它的子节点中一定有一个没选的点k,其它点随意.

f[i][1] = max{f[k][0] + 1 + Σmax{f[j][0],f[j][1] (j != k)}}.对于g的转移就是比较常见的套路了,子树内加法原理,子树合并乘法原理.每次选肯定要选最大匹配的那种方案,所以开一个数组ans,如果f[i][0] > f[i][1],ans[i] = g[i][0]; f[i][0] < f[i][1],ans[i] = g[i][1];

f[i][0] = f[i][1],ans[i] = g[i][1] + g[i][0]. 那么g[i][0] = πans[j],i要选的话,k就不能直接用ans的值,那么g[i][1] = Σ(g[k][0] * (πans[j] / ans[k])),涉及到取模,用到了乘法逆元.

设计状态的时候想清楚当前点有哪几种状态,它们对答案有没有影响.转移的时候想想转移过来的子节点必须满足什么要求,其它的点该怎么分配.求方案数的时候要分清楚是乘法原理还是加法原理.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm> using namespace std;
const int maxn = , mod = ; typedef long long ll; int T, P, n, head[maxn], to[maxn * ], nextt[maxn * ], tot = ;
ll f[maxn][], g[maxn][], ans[maxn]; void add(int x, int y)
{
to[tot] = y;
nextt[tot] = head[x];
head[x] = tot++;
} ll qpow(ll a, ll b)
{
ll res = ;
while (b)
{
if (b & )
res = (res * a) % mod;
a = (a * a) % mod;
b >>= ;
}
return res;
} void dfs(int x, int fa)
{
f[x][] = f[x][] = ans[x] = ;
g[x][] = g[x][] = ;
bool flag = false;
ll sum = , mul = ;
for (int i = head[x]; i; i = nextt[i])
{
int v = to[i];
if (v != fa)
{
dfs(v, x);
sum += max(f[v][], f[v][]);
sum %= mod;
f[x][] += max(f[v][], f[v][]);
f[x][] %= mod;
g[x][] = g[x][] * ans[v] % mod;
mul = mul * ans[v] % mod;
flag = true;
}
}
if (!flag)
g[x][] = ;
for (int i = head[x]; i; i = nextt[i])
{
int v = to[i];
if (v != fa)
{
if (f[x][] < f[v][] + + sum - max(f[v][], f[v][]))
{
f[x][] = (((f[v][] + + sum) % mod) - max(f[v][], f[v][]) + mod) % mod;
g[x][] = g[v][] * mul % mod * qpow(ans[v],mod - ) % mod;
}
else
if (f[x][] == f[v][] + + sum - max(f[v][], f[v][]))
g[x][] = (g[x][] + g[v][] * mul % mod * qpow(ans[v],mod - ) % mod) % mod;
}
}
if (f[x][] > f[x][])
ans[x] = g[x][];
if (f[x][] < f[x][])
ans[x] = g[x][];
if (f[x][] == f[x][])
ans[x] = g[x][] + g[x][];
ans[x] %= mod;
} int main()
{
scanf("%d%d", &T, &P);
while (T--)
{
memset(head, , sizeof(head));
tot = ;
scanf("%d", &n);
for (int i = ; i < n; i++)
{
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
dfs(, );
printf("%lld ", max(f[][], f[][]));
if (P == )
printf("%lld", ans[] % mod);
printf("\n");
}
return ;
}

最新文章

  1. 15 BasicHashTable基本哈希表类(二)——Live555源码阅读(一)基本组件类
  2. SQL递归查询(with cte as)
  3. (转)Git Gui for Windows的建库、克隆(clone)、上传(push)、下载(pull)、合并
  4. linux网络编程常用头文件
  5. c#接口定义与应用
  6. PHP练习项目笔记之COOKIES
  7. C#获取桌面壁纸图片的路径(Desktop Wallpaper)
  8. Leetcode_20_Valid Parentheses
  9. Redis集群管理
  10. Tree Requests CodeForces - 570D (dfs水题)
  11. day 74 json 和 ajax 的实例
  12. CSS3一个酷炫的加载效果
  13. hdu2068 RPG的错排 组合数/递推
  14. Linux Cluster环境下批量分发执行补丁
  15. MySql 赋值操作符&quot;=&quot;与&quot;:=&quot;
  16. [入门阅读]怎样在android中解析JSON
  17. Linux用户和权限管理
  18. C++ 四种显示转换
  19. python自动化 协程函数、二分查找、模块搜索
  20. jquery中的cookie使用

热门文章

  1. P3052 [USACO12MAR]摩天大楼里的奶牛Cows in a Skyscraper 状压dp
  2. 杂项-Java:JMX
  3. 【转载】SSH框架总结(框架分析+环境搭建+实例源码下载)
  4. PCB MS SQL 存储过程(CLR) 实现Json转DataTable表的方法
  5. 【WIP】Ruby&#160;CSV文件操作
  6. Django day29 视图,路由控制,响应器
  7. @RequestParam 和 @RequestBody 接受的时间格式
  8. jQuery 事件 - trigger() 方法 和 triggerHandler() 方法
  9. 浏览器被“hao123.3377.com”主页劫持的解决办法
  10. Laravel5.1学习笔记20 EloquentORM 关系