zoukankan      html  css  js  c++  java
  • [2020 NOI Online]游戏

    Desciption

    树上有 (2n) 个点,每个点属于两个集合中的一个,且集合大小为 (n)。将两个集合里的点配对,使得恰好有 (k) 对从属关系,即一个点在另一个点的子树里。

    Solution

    已经没有什么好害怕的了这道题很像,都是点的配对,然后问有多少形成某种偏序关系的方案。前者的偏序关系就是实数的比较,所以容易想到按值排序,然后线性 dp。

    类似的,这道题的偏序关系构成一棵树,所以想到用树形 dp 统计方案。定义 (dp_{u,j}) 表示在以 (u) 为根的子树中,有 (j) 对从属关系的方案数。

    那么容易想到有两种转移方式。第一种是所有从属关系直接从子节点获得,背包即可。第二种是节点 (u) 和子树内的某个点配对,考虑到状态 (dp_{u,j}) 保证了至少有 (j) 对点被选,也即一个集合被选了 (j) 个元素,那么剩下的可选的元素个数就可以通过集合大小减去 j 来获得。而集合大小容易统计。

    钦定 (j) 对从属关系的可重方案就为 (F_j=dp_{1,j} imes(n-j)!),反演一下即为答案。

    #include<stdio.h>
    #define N 7007
    #define ll long long
    #define Mod 998244353
    
    inline int read(){
        int x=0,flag=1; char c=getchar();
        while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
        return flag? x:-x;
    }
    
    int n;
    char S[N];
    
    struct E{
        int next,to;
    }e[N<<1];
    int head[N],cnt=0,sz[N],s[2][N],fa[N],m;
    ll fac[N],inv[N];
    
    ll qpow(ll x,ll y){
        ll ret=1,tot=0;
        while(y>=(1ll<<tot)){
            if(y&(1ll<<tot)) ret=ret*x%Mod;
            x=x*x%Mod,tot++;
        }
        return ret;
    }
    
    inline void add(int id,int to){
        e[++cnt]=(E){head[id],to};
        head[id]=cnt;
    }
    
    ll dp[N][N];
    void dfs(int u){
        dp[u][0]=1;
        sz[u]=1,s[u][S[u]-'0']=1;
        static ll tmp[N];
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to;
            if(v==fa[u]) continue;
            fa[v]=u,dfs(v);
            for(int j=0;j<=m;j++) tmp[j]=0;
            for(int j=sz[u];~j;j--)
                for(int k=sz[v];~k;k--)
                    tmp[j+k]=(tmp[j+k]+dp[u][j]*dp[v][k]%Mod)%Mod;
            for(int j=0;j<=m;j++) dp[u][j]=tmp[j];
            sz[u]+=sz[v];
            s[u][0]+=s[v][0],s[u][1]+=s[v][1];
        }
        int to=(S[u]-'0')^1;
        for(int i=s[u][to];i;i--)
            dp[u][i]=(dp[u][i]+dp[u][i-1]*(s[u][to]-i+1)%Mod)%Mod;
    }
    
    ll C(int x,int y){return x<y? 0:fac[x]*inv[y]%Mod*inv[x-y]%Mod;}
    
    int main(){
        n=read(),m=n>>1;
        scanf("%s",S+1);
        for(int i=1;i<n;i++){
            int u=read(),v=read();
            add(u,v),add(v,u);
        }
        dfs(1); fac[0]=1;
        for(int i=1;i<=m;i++) fac[i]=fac[i-1]*i%Mod;
        inv[m]=qpow(fac[m],Mod-2);
        for(int i=m-1;~i;i--) inv[i]=inv[i+1]*(i+1)%Mod;
        for(int i=0;i<=m;i++) dp[1][i]=dp[1][i]*fac[m-i]%Mod;
        for(int i=0;i<=m;i++){
            ll ans=0;
            for(int k=i,op=1;k<=m;k++,op=-op)
                ans=(ans+op*C(k,i)*dp[1][k]%Mod+Mod)%Mod;
            printf("%lld
    ",ans);
        }
    }
    
  • 相关阅读:
    JavaScript-数学对象与定时器
    JavaScript(八)-字符串与数组
    嵌入式的笔试题目(1)
    更改登录使用的默认shell的方法
    查看当前Linux 命令行使用的shell 的方法
    启动引导程序 Bootloader
    Debian 系(Deepin, Ubuntu, Linuxmint等)包管理工具
    ubuntu 服务器 samba 局域网内 如何添加samba user
    win10 和 树莓派3b+ 处于同一wifi环境(同一网段), win10 无法ping 通 树莓派3b+
    数据结构概念
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/14613336.html
Copyright © 2011-2022 走看看