! NOIonline2T3游戏

数据范围5000

题目链接

\(f[i]\)表示至少选\(i\)对祖孙关系的方案数,然后容斥一下就好了

\(g[x][i]\)表示在\(x\)的子树选\(i\)对的方案数,如果不选自己就是一个背包

选自己的方案数为子树内可以与自己匹配的点的数量

最后\(f[i]=g[x][i]*fac[n/2-i]\)

然后怎么容斥都不对……

\(f[j]\)会在\(ans[i]\)种算\(C_j^i\)次(像这种与阶乘有关的

\[ans[i]=\sum_{j=i}^{n/2}C_j^if[j] \]

二项式反演

\[f[i]=\sum_{j=i}^n(-1)^{j-i}C_j^ig[j] \]

可以用斯特林数优化,但其实直接暴力即可

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
#define ll long long
const int N=5004,mod=998244353;
int n,s0[N],s1[N],tmp[N],g[N][N],c[N][N];
vector<int>e[N];
char ch[N];
void dfs(int x,int fa){
	g[x][0]=1;
	for(auto v:e[x]){
		if(v==fa)continue;
		dfs(v,x);
		for(int i=min(s0[x],s1[x]);i>=0;i--)
			for(int j=min(n/2-i,min(s0[v],s1[v]));j>=0;j--)
				tmp[i+j]=((ll)g[v][j]*g[x][i]+tmp[i+j])%mod;
		for(int i=0;i<=n/2;i++){g[x][i]=tmp[i];tmp[i]=0;}
		s0[x]+=s0[v];s1[x]+=s1[v];
	}
	if(ch[x]=='1'){
		s1[x]++;
		for(int i=s0[x];i;i--)
			g[x][i]=((ll)g[x][i-1]*(s0[x]-i+1)+g[x][i])%mod;
	}
	else{
		s0[x]++;
		for(int i=s1[x];i;i--)
			g[x][i]=((ll)g[x][i-1]*(s1[x]-i+1)+g[x][i])%mod;
	}
}
int main(){
	n=read();
	scanf("%s",ch+1);
	for(int i=1,u,v;i<n;i++){
		u=read();v=read();
		e[u].push_back(v);e[v].push_back(u); 
	}	
	for(int i=0;i<=n/2;i++){
		c[i][0]=1;
		for(int j=1;j<=i;j++)
			c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
	}
	dfs(1,0);
	for(int i=n/2-2,j=2,fac=1;i>=0;i--,j++){
		fac=(ll)fac*j%mod;
		g[1][i]=(ll)g[1][i]*fac%mod;
	}
	for(int i=0,ans;i<=n/2;i++){
		ans=0;
		for(int j=i;j<=n/2;j++)
			ans=((ll)((j-i&1)?mod-1:1)*c[j][i]%mod*g[1][j]+ans)%mod;
		cout<<ans<<"\n";
	}
	return (0-0);
}

附:\(n^2\)背包(P2014选课)

  1. 把自己传给儿子
//设dp[i][j]表示选择以i为根的子树中j个节点。
//u代表当前根节点,tot代表其选择的节点的总额。
void dfs(int u,int tot)
{
    for(int i=head[x];i;i=e[i].next)
    {
        int v=e[i].to;
        for(int k=0;k<tot;k++)//这里k从o开始到tot-1,因为v的子树可以选择的节点是u的子树的节点数减一
            dp[v][k]=dp[u][k]+val[u];
        dfs(v,tot-1)
        for(int k=1;k<=tot;k++)
            dp[u][k]=max(dp[u][k],dp[v][k-1]);//这里是把子树的值赋给了根节点,因为u选择k个点v只能选择k-1个点。
    }
}
  1. 转后序遍历
f[i][j]=max(f[i-1][j-w[i]]+v[i],f[i-sz[i]][j]);//不选自己则跳过子树,选自己则继承上一个的位置DP更新
posted @ 2020-04-26 09:26  starusc  阅读(146)  评论(0)    收藏  举报