TC SRM421 R1 Div1 TeamManagement(树形 dp + 计数 dp)

Description

n n n 个球员,有的球员对俱乐部忠诚,有的球员对俱乐部不忠诚。有的球员是朋友,朋友的关系是一棵无根树,具有对称性但不具有传递性,每个球员最多有三个朋友。想购买一个球员,需要满足这个球员是忠诚的或这个球员的朋友被购买。俱乐部想购买 k k k 个球员,求每个球员被买到的概率。

1 ≤ k ≤ n ≤ 50 1 \leq k \leq n \leq 50 1kn50

Solution

有一个不同寻常的性质:每个球员最多有三个朋友。所以选择一个度为以的点 r t rt rt 为根,每个点最多有两个儿子。,

进行树形 dp。令 f x , c n t , t y p f_{x,cnt,typ} fx,cnt,typ 为以 x x x 为根的子树中,选择 c n t cnt cnt 个点的方案数。定义根据 t y p typ typ 的值略有改动:

  • t y p = 0 typ = 0 typ=0: 无改动。
  • t y p = 1 typ = 1 typ=1 x x x 与子树外的忠诚点相连。
  • t y p = 2 typ=2 typ=2 x x x 与子树内的忠诚点相连。

考虑转移。

x x x 为叶子节点, c n t ≤ 1 cnt \leq 1 cnt1

c n t = 0 cnt = 0 cnt=0 时: t y p = 0 , 1 , 2 typ = 0,1,2 typ=0,1,2 对应的 1 , 1 , 0 1,1,0 1,1,0。当 c n t = 1 cnt = 1 cnt=1 时: x x x 为忠诚的为 1 1 1 x x x 不忠诚时: t y p = 0 , 1 , 2 typ = 0,1,2 typ=0,1,2 对应的 0 , 1 , 0 0,1,0 0,1,0

x x x 为非叶子节点 且 x x x 有两个儿子 y 1 y_1 y1 y 2 y_2 y2
  • 不选择 x x x

    如果以 y 1 y_1 y1 为根的子树选择了 i i i 个点,那么 y 2 y_2 y2 为根的子树需要选择 c n t – i cnt – i cnti 个点。根据乘法原理为 f y 1 , i , 0 × f y 2 , c n t − i , 0 f_{y_1,i,0} \times f_{y_2,cnt-i,0} fy1,i,0×fy2,cnti,0。对 i ∈ [ 0 , c n t ] i \in [0,cnt] i[0,cnt] 的答案求和。

  • 选择 x x x

    • t y p = 1 typ = 1 typ=1 x x x 是忠诚的。对于 y 1 y_1 y1 y 2 y_2 y2 它们连到了某个子树外忠诚点(可以是 x x x),所以答案是 f y 1 , i , 1 × f y 2 , c n t − i − 1 , 1 f_{y_1,i,1} \times f_{y_2,cnt-i-1,1} fy1,i,1×fy2,cnti1,1 的总和。

    • t y p ≠ 1 typ \not= 1 typ=1 x x x 不忠诚。想要选择 x x x,必须让 x x x 与忠诚点相连,所以在 y 1 y_1 y1 y 2 y_2 y2 的中,要有至少一个 t y p = 2 typ = 2 typ=2。看上去答案是: f y 1 , i , 2 × f y 2 , c n t − i − 1 , 1 + f y 1 , i , 1 × f y 2 , c n t − i − 1 , 2 f_{y_1,i,2} \times f_{y_2,cnt-i-1,1}+ f_{y_1,i,1} \times f_{y_2,cnt-i-1,2} fy1,i,2×fy2,cnti1,1+fy1,i,1×fy2,cnti1,2。但是 t y p = 2 typ = 2 typ=2 的答案当 y 1 y_1 y1 y 2 y_2 y2 两个子树中都有忠诚节点是会被重复计算,所以答案为: f y 1 , i , 2 × f y 2 , c n t − i − 1 , 1 + f y 1 , i , 1 × f y 2 , c n t − i − 1 , 2 − f y 1 , i , 2 × f y 2 , c n t − i − 1 , 2 f_{y_1,i,2} \times f_{y_2,cnt-i-1,1}+ f_{y_1,i,1} \times f_{y_2,cnt-i-1,2} - f_{y_1,i,2} \times f_{y_2, cnt-i-1,2} fy1,i,2×fy2,cnti1,1+fy1,i,1×fy2,cnti1,2fy1,i,2×fy2,cnti1,2

至于怎么计算概率。可以禁止一个点被选,求出方案数,用 1 − 1- 1 禁止与不禁止的方案数之比求概率。

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 50 + 5; 
class TeamManagement {	
	private:
		int n, k;
		int a[N][N], deg[N], canTake[N], isLoyal[N]; 
		ll f[N][N][3];
	public:
		void dfs(int x, int fa) {
			// find children
			vector <int> cd;
			for (int y = 0; y < n; y++) if (a[x][y] && y != fa) {
				dfs(y, x); cd.push_back(y); 
			}
			int sz = cd.size();
			if (sz == 0) { // leaf vertex
				if (isLoyal[x]) {
					f[x][0][0] = 1, f[x][1][0] = (canTake[x] ? 1 : 0);
					f[x][0][1] = 1, f[x][1][1] = (canTake[x] ? 1 : 0);
					f[x][0][2] = 0, f[x][1][2] = (canTake[x] ? 1 : 0);
				} else {
					f[x][0][0] = 1, f[x][1][0] = 0;
       	 			f[x][0][1] = 1, f[x][1][1] = (canTake[x] ? 1 : 0);
        			f[x][0][2] = 0, f[x][1][2] = 0;
				}
				return ;
			}
			//Don't take x
			for (int i = 0; i <= k; i++) {
				if (sz == 1) f[x][i][0] += f[cd[0]][i][0], f[x][i][1] += f[cd[0]][i][0];
				else {
					for (int j = 0; j <= i; j++) {
						f[x][i][0] += f[cd[0]][j][0] * f[cd[1]][i - j][0],
						f[x][i][1] += f[cd[0]][j][0] * f[cd[1]][i - j][0];
					}
				}
			}
			// Take x 
			if (!canTake[x]) return ;
			for (int i = 1; i <= k; i++)
				for (int typ = 0; typ <= 2; typ++) 
					if (typ == 1 || isLoyal[x]) {
						if (sz == 1) f[x][i][typ] += f[cd[0]][i - 1][1];
						else for (int j = 0; j < i; j++) f[x][i][typ] += f[cd[0]][j][1] * f[cd[1]][i - j - 1][1];
					} else {
						if (sz == 1) f[x][i][typ] += f[cd[0]][i - 1][2];
						else for (int j = 0; j < i; j++) f[x][i][typ] += f[cd[0]][j][1] * f[cd[1]][i - j - 1][2] 
						+ f[cd[0]][j][2] * f[cd[1]][i - j - 1][1] - f[cd[0]][j][2] * f[cd[1]][i - j - 1][2]; 
					}
		}
		vector <double> getDistribution (int _n, int _k, vector <string> friends, string loyal) {
			n = _n, k = _k;
			for (int i = 0; i < n; i++) isLoyal[i] = (loyal[i] == 'Y');
			for (int i = 0; i < n - 1; i++) {
				istringstream sin(friends[i]);
				int x, y; sin >> x >> y;
				a[x][y] = a[y][x] = 1; 
				deg[x]++, deg[y]++;
			}
			int rt = 0;
			while (deg[rt] > 1) rt++; //Take a leaf as  a root 
			memset(canTake, 1, sizeof(canTake)); memset(f, 0, sizeof(f));
			vector <double> ans; 
			dfs(rt, -1); ll tot = f[rt][k][0];
			for (int i = 0; i < n; i++) {
				memset(f, 0, sizeof(f));
				canTake[i] = 0; dfs(rt, -1); canTake[i] = 1;
				ans.push_back(1.0 - (f[rt][k][0] * 1.0 / tot));
			}
			return ans;
		}
}Solve;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值