zoukankan      html  css  js  c++  java
  • 【CTS2019】氪金手游(动态规划)

    【CTS2019】氪金手游(动态规划)

    题面

    LOJ
    洛谷

    题解

    首先不难发现整个图构成的结构是一棵树,如果这个东西是一个外向树的话,那么我们在意的只有这棵子树内的顺序关系,子树外的关系与这棵子树之间的限制无关。所以我们只需要强制根节点在其他儿子之前的就行了(你可以认为如果这次随机抽到了子树外面的东西就重新抽一次,这个概率等于只考虑子树权值和的概率),那么这里的概率就是(frac{w_u}{sum w})。然后每个根节点显然可以独立考虑,所以只需要把所有根节点的结果直接乘起来就好了。
    那么对于(w)也有概率的情况,设(f[i][w])表示以(i)为根的子树中,权值和为(w)时根节点合法的概率。
    这个随便转移一下就很好做了。
    现在加上了反向边,反向边强制了儿子要在根节点之前出现,而状态也只要两种,要么反向边在前要么反向边在后,那么设(f[i][w][j])表示以(i)为子树,子树和为(w),至少有(j)条反向边不满足条件的概率,既然强制了若干个不反向,那么就是你枚举一些边,然后强制把它变成正向边,剩下的反向边直接删掉,这样子就可以求出这个概率。
    注意到这个容斥的系数就是简单的(pm 1),所以只需要直接把容斥系数带进去算就行了。
    这样子复杂度可以做到(O(n^2))

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define MOD 998244353
    #define MAX 1010
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;}
    struct Line{int v,next;}e[MAX<<1];
    int h[MAX],cnt=1;
    inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
    int f[MAX][3*MAX],sz[MAX],p[MAX][4],inv[MAX*3],tmp[MAX*3],n,ans;
    void dfs(int u,int ff)
    {
    	sz[u]=1;
    	for(int i=h[u];i;i=e[i].next)
    	{
    		int v=e[i].v;if(v==ff)continue;
    		dfs(v,u);
    		for(int j=0;j<=3*sz[u];++j)
    			for(int k=0;k<=3*sz[v];++k)
    			{
    				int val=1ll*f[u][j]*f[v][k]%MOD;
    				if(i&1)tmp[j+k]=(tmp[j+k]+val)%MOD;
    				else tmp[j+k]=(tmp[j+k]+MOD-val)%MOD,tmp[j]=(tmp[j]+val)%MOD;
    			}
    		sz[u]+=sz[v];for(int j=0;j<=3*sz[u];++j)f[u][j]=tmp[j],tmp[j]=0;
    	}
    	for(int j=0;j<=sz[u]*3;++j)f[u][j]=1ll*f[u][j]*inv[j]%MOD;
    }
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;++i)
    	{
    		int a1=read(),a2=read(),a3=read();
    		int inv=fpow(a1+a2+a3,MOD-2);
    		f[i][1]=1ll*a1*inv%MOD;
    		f[i][2]=2ll*a2*inv%MOD;
    		f[i][3]=3ll*a3*inv%MOD;
    	}
    	for(int i=1;i<n;++i)
    	{
    		int u=read(),v=read();
    		Add(u,v);Add(v,u);
    	}
    	inv[0]=inv[1]=1;for(int i=2;i<=3*n;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
    	dfs(1,0);for(int i=0;i<=3*n;++i)ans=(ans+f[1][i])%MOD;
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    简单明了的带你理解springboot原理和三大核心注解
    Spring Boot(一):入门篇
    【Mysql优化】聚簇索引与非聚簇索引概念
    Mysql索引原理与优化
    Mysql全文索引的使用
    索引的优缺点,如何创建索引
    184 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 04 例:字符串与byte(即:字节)数组间的相互转换
    183 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 03 String常用方法(下)
    182 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 02 String常用方法(上)
    181 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 01 String常用方法简介
  • 原文地址:https://www.cnblogs.com/cjyyb/p/10914521.html
Copyright © 2011-2022 走看看