题目链接:http://codeforces.com/contest/461/problem/B

题目大意:

  给定一课树,树上的节点有黑的也有白的,有这样一种分割树的方案,分割后每个子图只含有一个黑色节点,问这样的分割方案一共有多少种?

分析:

先定义3个函数(为了之后说起来方便):

  设A(x) = 在以顶点x为根的子树中,所有满足使x所在子图不含黑色顶点,其余子图只含有1个黑色顶点的分割方案种数 。
  设B(x) = 在以顶点x为根的子树中,所有满足每个子图只含有1个黑色顶点的分割方案种数 。
  设C(x) = 在以顶点x为根的子树中,所有满足使x所在子图只含有1个黑色顶点,其余子图只含有1个黑色顶点的分割方案种数 。
  显然B(x) = C(x)
  首先我们关注某一课子树的根节点,不妨设为rt,有2种情况:
  不妨设rt有a个黑色的孩子节点,b个白色的孩子节点,rt的第i个黑色节点表示为rtBi,白色节点表示为rtWi,rt的第i个孩子节点表示为soni
1 : color[rt] == WHITE
  初始条件下B(rt) = 0
  在这种情况下,rt与每个孩子节点都有相连与不相连2种策略,对于soni,不相连的贡献自然是B(soni),而相连的贡献就不是独立的了,很可能有跨越根节点到其他子树的方案,也就是说,对于每一棵子树,都要和其他每一棵子树有深入交流。
  我们可以考虑增而治之,设rtSonsi为以rt为根节点,只拥有前i个孩子节点的树,A(rt),B(rt),C(rt)为相应数值,初始状态为A(rt) = 1,B(rt) = 0,C(rt) = 0,我们只要考虑soni+1加入时,A(rt),B(rt),C(rt)会如何变化,当子树全部加入时,就算完了。这里有2种情况:
  (1)与soni+1相连:B(rt)  = C(rt) = A(rt) * C(soni+1) + C(rt) * A(soni+1)
                                    A(rt) = A(rt) * A(soni+1)
  (2)与soni+1不相连:B(rt)  = C(rt) = C(rt) * C(soni+1)
                                        A(rt) = A(rt) * C(soni+1)
  于是总变化为:B(rt)  = C(rt) = A(rt) * C(soni+1) + C(rt) * [A(soni+1) + C(soni+1)]

                           A(rt) = A(rt) * [A(soni+1) + C(soni+1)]
 
 

2:color[rt] == BLACK
  初始条件下B(rt) = 1,自己自身算一个
  在这种情况下,rt一定不能与黑色的孩子节点相连,而对于白色的孩子节点,有相连与不相连两种选择。
  同样增而治之,设rtSonsi为以rt为根节点,只拥有前i个孩子节点的树,A(rt),B(rt),C(rt)为相应数值,初始状态为A(rt) = 0,B(rt) = 1,C(rt) = 1并且A(rt)恒等于0,我们只要考虑soni+1加入时,A(rt),B(rt),C(rt)会如何变化,当子树全部加入时,就算完了。这里有3种情况:
  (1)soni+1为白色节点&&与soni+1相连:B(rt)  = C(rt) = C(rt) * A(soni+1)
  (2)soni+1为白色节点&&与soni+1不相连:B(rt)  = C(rt) = C(rt) * C(soni+1)
  (3)soni+1为黑色节点(和2是同一种情况):B(rt)  = C(rt) = C(rt) * C(soni+1)
  于是总变化为:B(rt)  = C(rt) = C(rt) * [A(soni+1) + C(soni+1)]

 
一个优化:对于color[rt] == BLACK的情况,如果我们把rt看成是白色的,然后照此计算出A(rt),B(rt),C(rt),那么以rt为根节点的子树的真实方案数,不就是A(rt)吗?这样的话就没有必要分情况了,在最后加个判断即可。

 

代码如下:

 #include <bits/stdc++.h>
using namespace std; #define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define rFor(i,t,s) for (int i = (t); i >= (s); --i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)
#define rforeach(i,c) for (__typeof(c.rbegin()) i = c.rbegin(); i != c.rend(); ++i) #define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl #define LOWBIT(x) ((x)&(-x)) #define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin()) #define ms0(a) memset(a,0,sizeof(a))
#define msI(a) memset(a,inf,sizeof(a))
#define msM(a) memset(a,-1,sizeof(a)) #define pii pair<int,int>
#define piii pair<pair<int,int>,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second inline int gc(){
static const int BUF = 1e7;
static char buf[BUF], *bg = buf + BUF, *ed = bg; if(bg == ed) fread(bg = buf, , BUF, stdin);
return *bg++;
} inline int ri(){
int x = , f = , c = gc();
for(; c<||c>; f = c=='-'?-:f, c=gc());
for(; c>&&c<; x = x* + c - , c=gc());
return x*f;
} typedef long long LL;
typedef unsigned long long uLL;
const int inf = 1e9 + ;
const LL mod = 1e9 + ;
const int maxN = 1e5 + ; struct Node{
vector< int > next;
}; int n, ans; /*
设A(x) = 在以顶点x为根的子树中,所有满足使x所在子图不含黑色顶点,其余子图只含有1个黑色顶点的分割方案种数
设B(x) = 在以顶点x为根的子树中,所有满足每个子图只含有1个黑色顶点的分割方案种数
设C(x) = 在以顶点x为根的子树中,所有满足使x所在子图只含有1个黑色顶点,其余子图只含有1个黑色顶点的分割方案种数
则有:
dp[v][0] = A(v) + B(v)
dp[v][1] = B(v)
C(v) = B(v)
The answer is dp[0][1].
*/
LL dp[maxN][];
Node nodes[maxN];
int color[maxN]; void dfs(int x) {
// 默认顶点x为白色
dp[x][] = ;
dp[x][] = ; foreach(i, nodes[x].next) {
dfs(*i);
// dp[*i][0] 包含 A(*i) 和 C(*i),对于A(*i),令其与C(x)相连
// 而对于C(*i),令其与C(x)分断
dp[x][] = (dp[x][] * dp[*i][]) % mod;
// dp[x][0] 包含 A(x),令其与C(*i)相连
dp[x][] = (dp[x][] + dp[x][] * dp[*i][]) % mod;
// 此时dp[x][0] = A(x)
// dp[x][0] * dp[*i][0] = A(x)*A(*i) + A(x)*C(*i)
// 依次是连接新子树的A(*i)部分和断掉新子树的C(*i)部分,两这都可以保证dp[x][0]更新后还是A(x)
dp[x][] = (dp[x][] * dp[*i][]) % mod;
} if(color[x] == ) dp[x][] = dp[x][]; // 当根节点为黑色时,A(x)实际上是C(x),而真正的A(x) = 0
else dp[x][] = (dp[x][] + dp[x][]) % mod; // 此时 dp[x][0] = A(x) + C(x)
} int main(){
while(cin >> n) {
rep(i, n) nodes[i].next.clear();
For(i, , n-) {
int t;
cin >> t;
// 根据题目要求,并不需要加反向边
nodes[t].next.push_back(i);
}
rep(i, n) cin >> color[i]; dfs(); cout << dp[][] <<endl;
}
return ;
}

CodeForces 461B Appleman and T的更多相关文章

  1. Codeforces 461B Appleman and Tree(木dp)

    题目链接:Codeforces 461B Appleman and Tree 题目大意:一棵树,以0节点为根节点,给定每一个节点的父亲节点,以及每一个点的颜色(0表示白色,1表示黑色),切断这棵树的k ...

  2. Codeforces 461B. Appleman and Tree[树形DP 方案数]

    B. Appleman and Tree time limit per test 2 seconds memory limit per test 256 megabytes input standar ...

  3. Codeforces 461B Appleman and Tree

    http://codeforces.com/problemset/problem/461/B 思路:dp,dp[i][0]代表这个联通块没有黑点的方案数,dp[i][1]代表有一个黑点的方案数 转移: ...

  4. Codeforces 461B Appleman and Tree:Tree dp

    题目链接:http://codeforces.com/problemset/problem/461/B 题意: 给你一棵树(编号从0到n-1,0为根节点),每个节点有黑白两种颜色,其中黑色节点有k+1 ...

  5. Codeforces 461B - Appleman and Tree 树状DP

    一棵树上有K个黑色节点,剩余节点都为白色,将其划分成K个子树,使得每棵树上都仅仅有1个黑色节点,共同拥有多少种划分方案. 个人感觉这题比較难. 如果dp(i,0..1)代表的是以i为根节点的子树种有0 ...

  6. CodeForces 462B Appleman and Card Game(贪心)

    题目链接:http://codeforces.com/problemset/problem/462/B Appleman has n cards. Each card has an uppercase ...

  7. codeforces 462C Appleman and Toastman 解题报告

    题目链接:http://codeforces.com/problemset/problem/461/A 题目意思:给出一群由 n 个数组成的集合你,依次循环执行两种操作: (1)每次Toastman得 ...

  8. CF 461B Appleman and Tree 树形DP

    Appleman has a tree with n vertices. Some of the vertices (at least one) are colored black and other ...

  9. Codeforces 263B. Appleman and Card Game

    B. Appleman and Card Game time limit per test  1 second memory limit per test  256 megabytes input  ...

随机推荐

  1. C#在截屏时将截屏之前需要隐藏的控件也截入

    最近我在项目中遇到一个让我十分头疼的问题,就是我在截屏时也将截屏之前隐藏的控件也截入了. 情况:我在Winform窗体有个截屏功能按钮,实现在调用WPF全屏后截屏,但在截屏WPF界面前将界面里的一个L ...

  2. 解决java web中safari浏览器下载后文件中文乱码问题

    解决java web中safari浏览器下载后文件中文乱码问题 String fileName = "测试文件.doc"; String userAgent = request.g ...

  3. charles抓包出现乱码 SSL Proxying not enabled for this host:enable in Proxy Setting,SSL locations

    1.情景:抓包的域名下 全部是unknown,右侧出现了乱码 2.查看unknown的notes里面:SSL Proxying not enabled for this host:enable in ...

  4. 测者的测试技术手册:揭开java method的一个秘密--巨型函数

    揭开java method的一个秘密:巨型函数 相信,很多人都不知道Java的Method的上限为64K.本文将超过这个上限的函数叫做巨型函数. 巨型函数的问题 1.如果代码超过了这个限制,Java编 ...

  5. python之生成随机密码

    https://www.cnblogs.com/evablogs/p/7096583.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #!/usr/bin/py ...

  6. 重装助手教你如何在Windows 10中更改您的帐户名称

    当您设置新的Win10免费下载 PC时,您选择用户名的部分可能会让您措手不及.如果是这种情况,您可以选择弹出头部的第一件事或者您打算稍后更改的随机和临时事物.但令人惊讶的是,在Windows 10中更 ...

  7. chrome总是提示“请停用开发者模式运行的扩展程序”

    方法1:通过组策略的扩展白名单.要下载一个组策略管理模板 1.开始 -> 运行 -> 输入gpedit.msc -> 回车确定打开计算机本地组策略编辑器(通过Win + R快捷键可以 ...

  8. 记录Vim常用命令

    命令 简单说明 i 进入编辑模式,光标在原位置 I 进入编辑模式,光标在行首位置 o 从光标所在行,下面一行开始编辑 O 从光标所在行,上面一行开始编辑 a 从光标当前字符后编辑 A 从光标所在行的行 ...

  9. Linux中删除特殊名称文件的多种方式

    今日分享:我们在肉体的疾病方面花了不少钱,精神的病害方面却没有花什么,现在已经到了时候,我们应该有不平凡的学校.--<瓦尔登湖> 前言 我们都知道,在linux删除一个文件可以使用rm命令 ...

  10. 18 python 初学(time、random 模块)

    # _author: lily # _date: 2019/1/13 import time import datetime print(help(time)) # print(time.time() ...