「CTS2019」氪金手游

解题思路

考场上想出了外向树的做法,居然没意识到反向边可以容斥,其实外向树会做的话这个题差不多就做完了。

令 \(dp[u][i]\) 表示单独考虑 \(u\) 节点所在子树,子树内 \(\sum w=i\) 的合法概率,可以简单证明子树外的选取是不影响子树内的答案的,所以可以这样表示。

证明:我们只考虑子树内的第一个选出根节点 \(u\) 的概率是 \(\frac{w_u}{i}\),假设当前未被选走的卡的概率之和为 \(S\) ,那么考虑全部未被选走的卡,子树内第一个选走 \(u\) 的概率为

\[\dfrac{w_u}{S}\sum_{k=0}^{\infty} (\dfrac{S-i}{S})^k
=\dfrac{w_u}{S}\times\dfrac{1}{1-\frac{S-i}{S}} \\
=\dfrac{w_u}{S} \times \frac{i}{S}= \frac{w_u}{i}
\]

两式相等,得证。

转移比较显然,因为是外向树,所以 \(u\) 要当中第一个被选中,剩下相当于对 \(w\) 做背包,直接从儿子合并即可。

考虑不是一棵外向树对其进行容斥,令 \(G(k)\) 表示有 \(k\) 条反向边被钦点为正向,剩下的反向边可反向也可正向的方案数,那么答案就是 \(\sum_{i=0}^m (-1)^i G(i)\) 。

发现可反向也可正向相当于把这条边删掉,两个连通块贡献用乘法原理合并,然后把容斥系数带到 \(dp\) 转移里面计算即可。

code

/*program by mangoyang*/
#include <bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int ch = 0, f = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
const int mod = 998244353;
vector<int> g[1005], d[1005];
int dp[1005][3005], sz[1005], w[1005][4], a[3005], n;
inline void up(int &x, int y){
x = x + y >= mod ? x + y - mod : x + y;
}
inline int Pow(int a, int b){
int ans = 1;
for(; b; b >>= 1, a = 1ll * a * a % mod)
if(b & 1) ans = 1ll * ans * a % mod;
return ans;
}
inline void gao(int u, int v, int type){
for(int i = 1; i <= 3 * sz[u]; i++)
for(int j = 1; j <= 3 * sz[v]; j++){
int x = 1ll * dp[u][i] * dp[v][j] % mod;
if(type) up(a[i], x);
up(a[i+j], type ? mod - x : x);
}
for(int i = 1; i <= 3 * (sz[u] + sz[v]); i++)
dp[u][i] = a[i], a[i] = 0;
sz[u] += sz[v];
}
inline void dfs(int u, int fa){
sz[u] = 1;
dp[u][1] = w[u][1], dp[u][2] = w[u][2], dp[u][3] = w[u][3];
for(auto v : g[u])
if(v != fa) dfs(v, u), gao(u, v, 0);
for(auto v : d[u])
if(v != fa) dfs(v, u), gao(u, v, 1);
for(int i = 1; i <= 3 * sz[u]; i++)
dp[u][i] = 1ll * dp[u][i] * Pow(i, mod - 2) % mod;
}
int main(){
read(n);
for(int i = 1, x, y, z; i <= n; i++){
read(x), read(y), read(z);
int s = Pow(x + y + z, mod - 2);
w[i][1] = 1ll * x * s % mod;
w[i][2] = 2ll * y * s % mod;
w[i][3] = 3ll * z * s % mod;
}
for(int i = 1, x, y; i < n; i++){
read(x), read(y);
g[x].push_back(y);
d[y].push_back(x);
}
dfs(1, 0);
int ans = 0;
for(int i = 1; i <= 3 * n; i++) up(ans, dp[1][i]);
cout << ans << endl;
return 0;
}

最新文章

  1. 十天精通CSS3学习笔记 part1
  2. java内存管理机制
  3. HDU 4768 Flyer(二分)
  4. Oracle TDE的数据加密示例并用logminer验证加密效果
  5. 后台增加一个左侧列表菜单menu菜单的方法
  6. iOS- 利用AFNetworking3.0+(最新AFN) - 实现文件断点下载
  7. BOM表生成
  8. @propetry参数
  9. 2015GitWebRTC编译实录4
  10. Cygwin之SSH服务安装过程问题
  11. 项目中用到的logback列子
  12. javascript debut trick, using the throw to make a interrupt(breakpoint) in your program
  13. python中的单下划线和双下划线意义和作用
  14. openssl编译(VC6.0)
  15. Mozilla 构建系统(转)
  16. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之五 || Swagger的使用 3.3 JWT权限验证【必看】
  17. 使用time+dd测试硬盘读写速度
  18. 利用BootStrap Table插件实现自己的弹出框分页。
  19. CodeBlocks: 生成的exe文件自定义一个图标
  20. 转一篇用分布式解决ERP问题

热门文章

  1. “知乎杯”2018 CCF 大学生计算机系统与程序设计竞赛 贪心算法(greedy)
  2. 2、kafka集群搭建
  3. 再见,Eclipse。
  4. linux df 日志删除命令分析
  5. 【转】理解Docker容器网络之Linux Network Namespace
  6. curl抓取页面时遇到重定向的解决方法
  7. clion 查看代码 多次查看后如何一步一步回退到最初查看的代码位置
  8. Unity3D Substance designer Sub 欧洲小镇场景制作视频教程 中文字幕
  9. java8之Spliterator
  10. RedisHelper Redis帮助类