zoukankan      html  css  js  c++  java
  • ! 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<<"
    ";
    	}
    	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更新
    
  • 相关阅读:
    VS,VAX一些快捷键记录
    UnrealScript中的Cpptext{}段落
    UScript在VS下的阅读及调试
    Unreal中的网络同步机制
    UDK编辑器 49条小提示(转)
    VS正则表达式常用篇
    Legacy:Within
    Hadoop + HBase + Hive 完全分布式部署笔记
    Windows批处理调用逻辑备份
    CentOS 下修改/dev/shm 大小解决ORA00845
  • 原文地址:https://www.cnblogs.com/aurora2004/p/12777166.html
Copyright © 2011-2022 走看看