zoukankan      html  css  js  c++  java
  • P5405-[CTS2019]氪金手游【树形dp,容斥,数学期望】

    前言

    话说在(Loj)下了个数据发现这题的名字叫(fgo)

    在这里插入图片描述


    正题

    题目链接:https://www.luogu.com.cn/problem/P5405


    题目大意

    (n)张卡的权值为(1/2/3)的概率权重分别是(p_{x,1/2/3}),然后按照权值每次获得一张未获得的卡,然后再该出一棵有向树(方向可以都是外向或内向的),求所有每条边((u,v))(u)都比(v)先获得的概率。

    (1leq nleq 1000,0leq p_{i,j}leq 10^6)


    解题思路

    只考虑外向树的话就是水题了,因为显然的(x)要排在子树最前面的概率就是(frac{w_x}{sum_{yin subtree_x}w_y})

    然后直接(n^2)(dp)就可以力。

    但是现在有内向边怎么办,还是考虑转换成只有外向的,也就是去掉一种限制。

    去掉一种限制的话容斥是一个不错的办法,考虑的话就是恰好若干条指定边(内向边),我们可以指定至少(k)跳内向边不满足条件,这样就组成了一个外向森林,可以很容易处理出答案,而且这样的容斥系数就是((-1)^k)

    然后直接(dp)就得了,设(f_{i,j})表示到节点(i)然后权值和是(j),如果限制一条内向边就直接乘上一个(-1)就好了。

    额这种树形(dp)枚举子树大小可以做到(n^2)这个是老生常谈了

    时间复杂度(O(n^2))


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const ll N=1100,P=998244353;
    struct node{
    	ll to,next,w;
    }a[N<<1];
    ll n,tot,ans,ls[N],siz[N],w[N][3],f[N][3*N],g[N*3];
    ll power(ll x,ll b){
    	ll ans=1;
    	while(b){
    		if(b&1)ans=ans*x%P;
    		x=x*x%P;b>>=1; 
    	}
    	return ans;
    }
    void addl(ll x,ll y,ll w){
    	a[++tot].to=y;
    	a[tot].next=ls[x];
    	a[tot].w=w;
    	ls[x]=tot;return;
    }
    void dp(ll x,ll fa){
    	ll d=power(w[x][0]+w[x][1]+w[x][2],P-2);
    	siz[x]=3;
    	f[x][1]=w[x][0]*d%P;
    	f[x][2]=w[x][1]*d*2ll%P;
    	f[x][3]=w[x][2]*d*3ll%P;
    	for(ll e=ls[x];e;e=a[e].next){
    		ll y=a[e].to;
    		if(y==fa)continue;
    		dp(y,x);
    		if(a[e].w){
    			for(ll i=1;i<=siz[x];i++)
    				for(ll j=1;j<=siz[y];j++)
    					(g[i+j]-=f[x][i]*f[y][j]%P)%=P,(g[i]+=f[x][i]*f[y][j]%P)%=P;
    		}
    		else{
    			for(ll i=1;i<=siz[x];i++)
    				for(ll j=1;j<=siz[y];j++)
    					(g[i+j]+=f[x][i]*f[y][j]%P)%=P;
    		}
    		siz[x]+=siz[y];
    		for(ll i=1;i<=siz[x];i++)
    			f[x][i]=g[i],g[i]=0;
    	}
    	for(int i=1;i<=siz[x];i++)
    		f[x][i]=f[x][i]*power(i,P-2)%P;
    	return;
    }
    signed main()
    {
    	scanf("%lld",&n);
    	for(ll i=1;i<=n;i++)
    		scanf("%lld%lld%lld",&w[i][0],&w[i][1],&w[i][2]);
    	for(ll i=1;i<n;i++){
    		ll x,y;scanf("%lld%lld",&x,&y);
    		addl(x,y,0);addl(y,x,1);
    	}
    	dp(1,0);
    	for(ll i=1;i<=siz[1];i++)
    		(ans+=f[1][i])%=P;
    	printf("%lld
    ",(ans+P)%P);
    	return 0;
    }
    
  • 相关阅读:
    Codeforces Beta Round #92 (Div. 2 Only) B. Permutations 模拟
    POJ 3281 Dining 最大流 Dinic算法
    POJ 2441 Arrange the BUlls 状压DP
    URAL 1152 Faise Mirrors 状压DP 简单题
    URAL 1039 Anniversary Party 树形DP 水题
    URAL 1018 Binary Apple Tree 树形DP 好题 经典
    pytorch中的forward前向传播机制
    .data()与.detach()的区别
    Argparse模块
    pytorch代码调试工具
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14974373.html
Copyright © 2011-2022 走看看