题目

题目大意

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

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

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

期望的最终结果。


思考历程

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

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

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

于是打暴力:

枚举所有的排列,然后用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. ionic-CSS:ionic Range

    ylbtech-ionic-CSS:ionic Range 1.返回顶部 1. ionic Range ionic Range 是一个滑块控件,ionic 为 Range 提供了很多种默认的样式.而且 ...

  2. 夏令营501-511NOIP训练18——高二学堂

    传送门:QAQQAQ 题意:给你一个数$n$,把它拆分成至多$k$个正整数,使得这些数的和等于$n$且每一个正整数的个数不能超过$4$ 拆分的顺序是无序的,但取出每一个数方案是不同的(例如我要拆$1$ ...

  3. 『BASH』——Hadex's brief analysis of "Lookahead and Lookbehind Zero-Length Assertions"

    /*为节省时间,本文以汉文撰写*/ -前言- 深入学习正则表达式,可以很好的提高思维逻辑的缜密性:又因正则应用于几乎所有高级编程语言,其重要性不言而喻,是江湖人士必备的内功心法. 正则表达式概要(ob ...

  4. .net与C#

    一..net包含什么? 1.包含庞大的代码库,分为多个模块,可以自主选择 2.定义了基本的类型,被称为通用类型系统(CTS,common type system): 3.包含.NET公共语言运行库(C ...

  5. 20140312 Excel表格画折现图次坐标轴

    1.struct 是8字节对齐 2.char * a, sizeof(a)=4; char a[]="abcdefg";sizeof(a)=8; 3.内存对其齐

  6. iOS 7.1的Safari为meta标签新增minimal-ui属性,在网页加载时隐藏地址栏与导航栏

    在 iOS 7.1 的 Safari 中为 meta 标签新增 minimal-ui 属性,让网页在加载时便可隐藏顶部的地址栏与底部的导航栏. 如何实现?你只需将“minimal-ui”加入 view ...

  7. Python+Django+ansible playbook自动化运维项目实战✍✍✍

    Python+Django+ansible playbook自动化运维项目实战  整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受 ...

  8. excel批量删除sql语句

    数据如下表一样 在E1列输入update sql 模板 ="update test set A= '"&A1&"' ,B = '"&B1 ...

  9. SQL Server 2008 install

    双击sql server 2008的.exe安装文件,进入[SQL Server 安装中心]. 2 点击界面左侧的[安装],然后点击右侧的[全新SQL Server 独立安装或向现有安装添加功能],进 ...

  10. (转)Android 升级 ADT 之后报错之一 case语句 .

    转:http://blog.csdn.net/wchinaw/article/details/7325641 下面文章大意是指:在一般的Android项目中,R类的常量都是用final定义的,但ADT ...