• 【洛谷6478】[NOI Online #2 提高组] 游戏(树形DP+二项式反演)


    点此看题面

    • 给定一棵以(1)为根的(n)个点的树((n=2m)),其中有(m)个黑点和(m)个白点。
    • 对于所有(k=1sim n),求有多少种黑点与白点配对的方案,满足恰有(k)对是祖先-后代关系。
    • (nle5 imes10^3)

    树形(DP)

    (g_{x,i})表示在(x)的子树中选出(i)对祖先-后代关系的方案数。

    首先,在不考虑(x)的情况下,子树之间只要做一遍树上背包就好了。

    然后考虑(x),我们可以维护出每个子树内两种颜色节点各自的数目,假设其中与(x)颜色不同的节点有(s)个。

    那么要从(g_{x,i-1})转移到(g_{x,i}),就有(s-(i-1))个点与(x)颜色不同,其实这便是这个转移的系数了。

    二项式反演

    (f_i)表示至少有(i)对祖先-后代关系的方案数,显然(f_i=g_{1,i} imes (m-i)!)(即钦定这(i)对,剩下的可以乱填)。

    那么容易发现一个基本关系式:

    [f_i=sum_{j=i}^nC_j^ians_j ]

    反手一个二项式反演:

    [ans_j=sum_{j=i}^n(-1)^{j-i}C_j^if_j ]

    于是这题就做完了。

    代码:(O(n^2))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 5000
    #define X 998244353
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    #define C(x,y) (1LL*Fac[x]*IFac[y]%X*IFac[(x)-(y)]%X)//组合数
    using namespace std;
    int n,m,a[N+5],f[N+5],ee,lnk[N+5];char p[N+5];struct edge {int to,nxt;}e[N<<1];
    I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
    int Fac[N+5],IFac[N+5];I void Init_F()//预处理阶乘和阶乘逆元
    {
    	RI i;for(Fac[0]=i=1;i<=n;++i) Fac[i]=1LL*Fac[i-1]*i%X;
    	for(IFac[n]=QP(Fac[n],X-2),i=n;i;--i) IFac[i-1]=1LL*IFac[i]*i%X;
    }
    int g[N+5][N+5],w[N+5],s[N+5][2];I void DP(CI x=1,CI lst=0)//树形DP
    {
    	RI i,j,k;for(g[x][0]=1,i=lnk[x];i;i=e[i].nxt)//树上背包
    	{
    		if(e[i].to==lst) continue;DP(e[i].to,x);//递归
    		for(j=w[x];~j;--j) for(k=1;k<=w[e[i].to];++k) g[x][j+k]=(1LL*g[x][j]*g[e[i].to][k]+g[x][j+k])%X;//注意卡上界保证复杂度
    		s[x][0]+=s[e[i].to][0],s[x][1]+=s[e[i].to][1],w[x]+=w[e[i].to];//更新子树信息
    	}
    	for(++s[x][p[x]&1],w[x]=min(s[x][0],s[x][1]),i=w[x];~i;--i)
    		g[x][i]=(1LL*g[x][i-1]*(s[x][p[x]&1^1]-(i-1))+g[x][i])%X;//x的转移
    }
    int main()
    {
    	RI i,j,x,y;for(scanf("%d%s",&n,p+1),m=n>>1,i=1;i^n;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x);
    	for(Init_F(),DP(),i=0;i<=m;++i) f[i]=1LL*g[1][i]*Fac[m-i]%X;//求出f[i]
    	RI t=0;for(i=0;i<=m;printf("%d
    ",t),++i)
    		for(t=0,j=i;j<=m;++j) t=(1LL*((j-i)&1?X-1:1)*C(j,i)%X*f[j]+t)%X;return 0;//二项式反演
    }
    
  • 相关阅读:
    asp的多国语言构思
    制作IE和FF都支持的无限级关联菜单
    破解网络尖兵(真正对付限制ADSL路由共享的方法)
    Asp透过系统DSN链接mysql数据库
    找到了一首曾经很喜欢的老歌
    生意人应具备的性格
    简单的操作让你的迅雷变的清爽
    线路分流自动跳转代码
    通过regsvr32注册DLL可以解决的一些疑难杂症
    页面无刷新超时自动退出
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6478.html
Copyright © 2020-2023  润新知