zoukankan      html  css  js  c++  java
  • [JZOJ5233] 【GDOI模拟8.5】概率博弈

    题目

    在这里插入图片描述

    题目大意

    给你一棵树,这棵树上的所有叶子节点的权值是随机的排列
    两个人博弈,从根开始,轮流往下走。
    先手希望权值最大,后手希望权值最小。
    期望的最终结果。


    思考历程

    这场比赛实在是太丧心病狂了!两个期望题,有没有人性啊!
    还是那种特别变态的期望……
    想了好久,没有想出来……
    于是打暴力:
    枚举所有的排列,然后用DP计算答案。
    如果喜欢,DP可以加上alpha-beta剪枝……当然我没加,因为在这题里面没有什么意义。
    分数还给的挺大方的,30分。


    正解

    首先推个式子:
    E(x)=xP(x=ans)=P(xans)E(x)\=sum xP(x=ans) \=sum P(x leq ans)
    后面这个是什么鬼?
    我们将第二个式子拆开,对于xx,它贡献了xxP(ans=x)P(ans=x)
    看看第三个式子,对于xxx'leq x,它就可以加上xx的贡献。这样的xx'xx个,所以这是成立的。

    接下来就是一个很巧妙的转化:
    我们设结果为xx,将大于xx的记作11,将小于等于xx的记作00
    这样就大大地简化了题目,因为我们只需要关心它的结果是否为11,而不需要关心结果是否恰好为xx
    然后就是树形DP:设fi,j,0/1f_{i,j,0/1}表示点ii为根的子树中,叶子节点的权值为00的个数是jj,点ii的值为0011(从下面转移上来的值)的方案数。
    方程就不用说了吧……自己推。
    这就是一个树上背包问题。
    至于最终的答案,枚举xx,它的贡献就是f1,x,1Cmxfrac{f_{1,x,1}}{C_m^x}。(mm为叶子节点的个数)
    由于题目良心地让我们输出ansm!ans*m!,所以说,输出的就是f1,x,1x!(mx)!f_{1,x,1}*x!*(m-x)!

    然而这个算法看上去是O(n3)O(n^3)的,我在很长时间内也这么认为。
    YMQ:我吸一口臭氧,也能过!
    但时间复杂度实际上是O(n2)O(n^2)
    为什么?
    从最简单的开始考虑:如果这棵树是一棵二叉树,对于一个非叶子节点,当大小分布比较均匀时:
    对于根节点,合并子树的时间是(n2)2=n24left(frac{n}{2} ight)^2=frac{n^2}{4}
    对于下一层,时间是2(n4)2=n282left(frac{n}{4} ight)^2=frac{n^2}{8}
    再下一层,时间是4(n8)2=n2164left(frac{n}{8} ight)^2=frac{n^2}{16}
    后面的就不枚举了。
    把它们全部加起来,时间就趋近于n22frac{n^2}{2}
    当大小分布不均匀时,我们考虑最极端的情况,时间复杂度还是O(n2)O(n^2)
    至于不均匀而又不极端的情况……感性理解,不信邪的可以打一个DP来求最坏的情况。
    考虑一下多叉树,发现其实际上是类似的,时间复杂度是O(n2)O(n^2)
    最终我还是不会证明……


    代码

    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
    ",ans);
    	return 0;
    }
    

    总结

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

    通过这个东西,可以大大地简化题目。
    当条件为“等于”的时候,我们可以试着转化成“大于”“小于”。
    然后就是树上背包的时间复杂度……

  • 相关阅读:
    css自适应浏览器大小
    javascript es6 箭头函数
    vue-router路由的使用
    vue-client脚手架使用
    springboot整合thymeleaf模板引擎
    SpringBoot不使用模板引擎直接返回html
    css加载动画
    java将数据从List转换Map
    KMP算法理解
    解决Linux服务器tomact-8.0启动慢的问题
  • 原文地址:https://www.cnblogs.com/jz-597/p/11145241.html
Copyright © 2011-2022 走看看