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;
    }
    
  • 相关阅读:
    C++面试题
    kalman滤波原理
    开blog
    Linux图片批处理
    Debian使用相关
    Ubuntu grub引导修复
    Debian系统vim中文显示乱码问题
    Debian配置Apache2支持mod-python和cgi模块
    linux下解压被分割的zip文件
    Ubuntu 16.04 启动错误 "a start job is running for hold until boot process finishes up"
  • 原文地址:https://www.cnblogs.com/cjyyb/p/10914521.html
Copyright © 2011-2022 走看看