题目

题目大意

给你一棵树,这棵树上的所有叶子节点的权值是随机的排列

两个人博弈,从根开始,轮流往下走。

先手希望权值最大,后手希望权值最小。

期望的最终结果。


思考历程

这场比赛实在是太丧心病狂了!两个期望题,有没有人性啊!

还是那种特别变态的期望……

想了好久,没有想出来……

于是打暴力:

枚举所有的排列,然后用DP计算答案。

如果喜欢,DP可以加上alpha-beta剪枝……当然我没加,因为在这题里面没有什么意义。

分数还给的挺大方的,30分。


正解

首先推个式子:

E(x)=∑xP(x=ans)=∑P(x≤ans)E(x)\\=\sum xP(x=ans) \\=\sum P(x \leq ans)E(x)=∑xP(x=ans)=∑P(x≤ans)

后面这个是什么鬼?

我们将第二个式子拆开,对于xxx,它贡献了xxx个P(ans=x)P(ans=x)P(ans=x)。

看看第三个式子,对于x′≤xx'\leq xx′≤x,它就可以加上xxx的贡献。这样的x′x'x′有xxx个,所以这是成立的。

接下来就是一个很巧妙的转化:

我们设结果为xxx,将大于xxx的记作111,将小于等于xxx的记作000。

这样就大大地简化了题目,因为我们只需要关心它的结果是否为111,而不需要关心结果是否恰好为xxx。

然后就是树形DP:设fi,j,0/1f_{i,j,0/1}fi,j,0/1​表示点iii为根的子树中,叶子节点的权值为000的个数是jjj,点iii的值为000或111(从下面转移上来的值)的方案数。

方程就不用说了吧……自己推。

这就是一个树上背包问题。

至于最终的答案,枚举xxx,它的贡献就是f1,x,1Cmx\frac{f_{1,x,1}}{C_m^x}Cmx​f1,x,1​​。(mmm为叶子节点的个数)

由于题目良心地让我们输出ans∗m!ans*m!ans∗m!,所以说,输出的就是f1,x,1∗x!∗(m−x)!f_{1,x,1}*x!*(m-x)!f1,x,1​∗x!∗(m−x)!

然而这个算法看上去是O(n3)O(n^3)O(n3)的,我在很长时间内也这么认为。

YMQ:我吸一口臭氧,也能过!

但时间复杂度实际上是O(n2)O(n^2)O(n2)。

为什么?

从最简单的开始考虑:如果这棵树是一棵二叉树,对于一个非叶子节点,当大小分布比较均匀时:

对于根节点,合并子树的时间是(n2)2=n24\left(\frac{n}{2}\right)^2=\frac{n^2}{4}(2n​)2=4n2​。

对于下一层,时间是2(n4)2=n282\left(\frac{n}{4}\right)^2=\frac{n^2}{8}2(4n​)2=8n2​。

再下一层,时间是4(n8)2=n2164\left(\frac{n}{8}\right)^2=\frac{n^2}{16}4(8n​)2=16n2​。

后面的就不枚举了。

把它们全部加起来,时间就趋近于n22\frac{n^2}{2}2n2​。

当大小分布不均匀时,我们考虑最极端的情况,时间复杂度还是O(n2)O(n^2)O(n2)。

至于不均匀而又不极端的情况……感性理解,不信邪的可以打一个DP来求最坏的情况。

考虑一下多叉树,发现其实际上是类似的,时间复杂度是O(n2)O(n^2)O(n2)。

最终我还是不会证明……


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 5000
#define mo 1000000007
int n;
struct EDGE{
int to;
EDGE *las;
} e[N*2+1];
int ne;
EDGE *last[N+1];
inline void link(int u,int v){
e[++ne]={v,last[u]};
last[u]=e+ne;
}
long long jc[N+1];
int tot[N+1];
long long f[N+1][N+1][2];
void dfs(int x,int fa,bool flag){
if (last[x]->las==NULL && last[x]->to==fa){//叶子节点的情况
tot[x]=1;
f[x][0][1]=f[x][1][0]=1;
return;
}
EDGE *ei=last[x];//另外处理第一个儿子,这样就不用考虑初始化
if (ei->to==fa)
ei=ei->las;
dfs(ei->to,x,!flag);
memcpy(f[x],f[ei->to],sizeof f[ei->to]);
tot[x]=tot[ei->to];
ei=ei->las;
if (flag==0){
for (;ei;ei=ei->las)
if (ei->to!=fa){
dfs(ei->to,x,1);
for (int j=tot[x]+tot[ei->to];j>=0;--j){
//k=0
f[x][j][1]=(f[x][j][0]*f[ei->to][0][1]%mo+f[x][j][1]*f[ei->to][0][0]%mo+f[x][j][1]*f[ei->to][0][1]%mo)%mo;
f[x][j][0]=f[x][j][0]*f[ei->to][0][0]%mo;
for (int k=1;k<=tot[ei->to] && k<=j;++k){
(f[x][j][1]+=(f[x][j-k][0]*f[ei->to][k][1]%mo+f[x][j-k][1]*f[ei->to][k][0]%mo+f[x][j-k][1]*f[ei->to][k][1]%mo))%=mo;
(f[x][j][0]+=f[x][j-k][0]*f[ei->to][k][0])%=mo;
}
}
tot[x]+=tot[ei->to];
}
}
else{
for (;ei;ei=ei->las)
if (ei->to!=fa){
dfs(ei->to,x,0);
for (int j=tot[x]+tot[ei->to];j>=0;--j){
//k=0
f[x][j][0]=(f[x][j][0]*f[ei->to][0][0]%mo+f[x][j][0]*f[ei->to][0][1]%mo+f[x][j][1]*f[ei->to][0][0]%mo)%mo;
f[x][j][1]=f[x][j][1]*f[ei->to][0][1]%mo;
for (int k=1;k<=tot[ei->to] && k<=j;++k){
(f[x][j][0]+=(f[x][j-k][0]*f[ei->to][k][0]%mo+f[x][j-k][0]*f[ei->to][k][1]%mo+f[x][j-k][1]*f[ei->to][k][0]%mo))%=mo;
(f[x][j][1]+=f[x][j-k][1]*f[ei->to][k][1])%=mo;
}
}
tot[x]+=tot[ei->to];
}
}
}
int main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
scanf("%d",&n);
jc[0]=1;
for (int i=1;i<=n;++i)
jc[i]=jc[i-1]*i%mo;
for (int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
link(u,v),link(v,u);
}
dfs(1,0,0);
long long ans=0;
for (int i=0;i<=tot[1];++i)
ans=(ans+f[1][i][1]*jc[i]%mo*jc[tot[1]-i]%mo)%mo;
printf("%lld\n",ans);
return 0;
}

总结

这道题有一个·很奇妙的思想,就在于∑xP(x=ans)=∑P(x≤ans)\sum xP(x=ans)=\sum P(x \leq ans)∑xP(x=ans)=∑P(x≤ans)。



通过这个东西,可以大大地简化题目。

当条件为“等于”的时候,我们可以试着转化成“大于”“小于”。

然后就是树上背包的时间复杂度……

[JZOJ5233] 【GDOI模拟8.5】概率博弈的更多相关文章

  1. GDOI模拟赛Round 1

    GDOI模拟赛Round 1 数据结构 题目描述:给出一个长度为\(n\)的序列,支持两种操作: 1.对某段区间都加上一个数 2.给出\(p.k\),求下面表示式对\((10^9+7)\)取模 \[\ ...

  2. 【JZOJ5233】【GDOI模拟8.5】概率博弈 树形dp+期望

    题面 小A和小B在玩游戏.这个游戏是这样的: 有一棵n个点的以1为根的有根树,叶子有权值.假设有m个叶子,那么树上每个叶子的权值序列就是一个1->m 的排列. 一开始在1号点有一颗棋子.两人轮流 ...

  3. 【NOIP模拟】jzoj5233概率博弈(树规)

    Description 小A和小B在玩游戏.这个游戏是这样的: 有一棵

  4. [jzoj5233]概率博弈(树形DP)

    Description 小A和小B在玩游戏.这个游戏是这样的: 有一棵

  5. 2018.10.17 NOIP模拟 发电机(概率dp)

    传送门 考试空间开大了爆零不然只有30分爆栈? 话说这题真的坑1e7没法写dfsdfsdfs 其实很好推式子. 考虑每个点安一个发动机的概率,推一波式子做个等比数列求和什么的可以证明出来是严格的1si ...

  6. Codeforces Round #417 (Div. 2)A B C E 模拟 枚举 二分 阶梯博弈

    A. Sagheer and Crossroads time limit per test 1 second memory limit per test 256 megabytes input sta ...

  7. NOIP模拟 赌博游戏 - 概率dp

    题意: 最近西雅图的高中校园里流行这样一个游戏. 我们有一个骰子,这个骰子有M个面,分别写着1..M,并且是个公平的骰子,换句话说,一次投掷时每个面朝上的概率是相同的. 游戏的组织者使用这个骰子进行N ...

  8. GDOI模拟4.11~4.13总结

    总体情况 省选前的第一场模拟,就连续三天垫底滚粗了. 三天下来,只做了第一天的签到题,然后再做了一些水题的暴力,还不得分. 三天分数:100/400+40/400+90/400=230/1200,得了 ...

  9. 7.12 NOI模拟赛 探险队 期望 博弈 dp 最坏情况下最优策略 可并堆

    LINK:探险队 非常难的题目 考试的时候爆零了 完全没有想到到到底怎么做 (当时去刚一道数论题了. 首先考虑清楚一件事情 就是当前是知道整张地图的样子 但是不清楚到底哪条边断了. 所以我们要做的其实 ...

随机推荐

  1. Keystone controller.py & routers.py代码解析

    目录 目录 Keystone WSGI 实现 controllerspy routerspy 参考文档 Keystone WSGI 实现 Keystone 项目把每个功能都分到单独的目录下,EXAMP ...

  2. 拾遗:不用使 sizeof 获取数组大小

    ... #include <stdio.h> #include <unistd.h> int main(void) { ] = {}; size_t num = () - (i ...

  3. 剑指offer——25合并两个排序的链表

    题目描述 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则.   题解: 使用普通方法,或者递归,注意新的头节点即可. //使用普通的合并方法 class S ...

  4. Codeforces 479【C】div3

    题目链接:http://codeforces.com/problemset/problem/977/C 题意:给你n个数字,输出任意一个数字,这个数字刚好大于等于,序列里面k个数字. 题解:排个序,第 ...

  5. Eclipse中普通java项目转成Web项目

    在eclipse导入一个myeclipse建的web项目后,在Eclipse中显示的还是java项目,按下面的步骤可以将其转换成web项目. 1.找到项目目录下的.project文件 2.编辑.pro ...

  6. 【模板篇】A* 寻路算法

    上次在做k短路的时候说到了A*, 但是并没有仔细的研究A*寻路, 毕竟k短路中的A*也不怎么标准… A*寻路的过程网上还是有很多的, 讲得也很清楚, 不妨跟着里面的图示自己动手操作一下, 基本一遍就能 ...

  7. 前端常用的库和实用技术之JavaScript多线程

    多线程概念: 多线程是指从软件或硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在 同一时间执行多于一个线程,进而提升整理处理性能.具有这种能力的系统包括对称多处理机,多核心 ...

  8. 一个切图仔的HTML笔记

    1,href="javascript:history.back(-1)" //页面返回上一步 2,meta信息设置 360浏览器就会在读取到这个标签后,立即切换对应的极速核. &l ...

  9. Java 虚拟机 - 2.3 HotSpot虚拟机对象

    对象的创建 Step1 类加载检查 当发现一条new指令时,检查: 该指令的参数是否能在常量池中定位到一个类的符号引用: 并且检查这个符号引用代表的类是否已经被加载.解析和初始化过.如果没有,那必须先 ...

  10. Java 多线程 - 原子操作AtomicInteger & CAS(Compare-and-Swap)

    原子类简介:https://www.cnblogs.com/stephen0923/p/4505902.html AtomicInteger 介绍: https://yuwenlin.iteye.co ...