[HNOI2015]实验比较

Description:

给你一些\(x < y\) ,\(x=y\) 的信息,求可能的原序列不同关系数
(每个\(x\)最多有一个\(x<y\) 的信息)

Hint:

$ n \le 100 $

Solution:

知道是树型dp,但还是不会

注意到等于的信息用并查集直接算作一个点就行,现在就是考虑这个<的条件

显然会形成一棵树,怎么算不同方案呢?

我们设\(f[i][j]\) 表示i的子树的序列用"<"分成了j段的方案数

这样dp可以很方便的合并子树信息:

考虑现在合并f[v1][x],f[v2][y],则第一段显然为i本身

剩下的j-1段由v1选x段,由于非空,再剩下的都选v2,v2多出来的再选

所以:

\[f[i][j]=f[v1][x]*f[v2][y]*C_{j-1}^{x-1}*C_{x-1}^{y-i+x} \]

(为什么是x-1,因为背包包含了i)

就是个树型背包

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ls p<<1 
#define rs p<<1|1
using namespace std;
typedef long long ll;
const int mxn=1e2+5,mod=1e9+7;
int n,m,cnt,ans,vis[mxn],id[mxn],eq[mxn],in[mxn],hd[mxn+10];
int c[mxn+5][mxn+5],g[mxn],f[mxn][mxn],sz[mxn],fa[mxn];
int x[mxn],y[mxn];

inline int read() {
    char c=getchar(); int x=0,f=1;
    while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
    while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
    return x*f;
}
inline void chkmax(int &x,int y) {if(x<y) x=y;}
inline void chkmin(int &x,int y) {if(x>y) x=y;}

struct ed {
    int to,nxt;
}t[mxn<<1];

inline void add(int u,int v) {
    t[++cnt]=(ed) {v,hd[u]}; hd[u]=cnt;
}

int find(int x) {
    return fa[x]==x?x:fa[x]=find(fa[x]);
}

void dfs(int u,int fa) {
    sz[u]=f[u][1]=1;
    for(int e=hd[u];e;e=t[e].nxt) {
        int v=t[e].to; if(v==fa) continue ; dfs(v,u);
        for(int i=1;i<=n;++i) g[i]=0;
        for(int i=1;i<=sz[u]+sz[v];++i) 
            for(int j=1;j<=sz[u];++j)
                for(int k=1;k<=sz[v];++k) {
                    int x=k-i+j; if(x<0) continue ;
                    g[i]=(g[i]+1ll*f[u][j]*f[v][k]%mod*c[i-1][j-1]%mod*c[j-1][x]%mod)%mod;
                }
        for(int i=1;i<=sz[u]+sz[v];++i) f[u][i]=g[i];		
        sz[u]+=sz[v];
    }
}

int main()
{
    n=read(); m=read(); 
    for(int i=0;i<=mxn;++i) c[i][0]=c[i][i]=1;
    for(int i=1;i<=mxn;++i) 
        for(int j=1;j<i;++j)
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
    for(int i=1;i<=n;++i) fa[i]=i;
    for(int i=1;i<=m;++i)	
        x[i]=read(),eq[i]=(getchar()=='='),y[i]=read();
    for(int i=1;i<=m;++i) 
        if(eq[i]) if(find(x[i])!=find(y[i])) fa[find(y[i])]=find(x[i]);
    for(int i=1;i<=n;++i) 
        vis[id[i]=find(i)]=1;
    for(int i=1;i<=n;++i) fa[i]=i;	
    for(int i=1;i<=m;++i) {
        if(!eq[i]) {
            add(id[x[i]],id[y[i]]),add(id[y[i]],id[x[i]]); ++in[id[y[i]]];
            if(find(id[x[i]])==find(id[y[i]])) 
                puts("0"),exit(0);
            else fa[find(id[y[i]])]=find(id[x[i]]);
        }
    }
    for(int i=1;i<=n;++i) 
        if(vis[i]&&!in[i]) add(n+1,i),add(i,n+1); dfs(n+1,0); 
    for(int i=1;i<=sz[n+1];++i) ans=(ans+f[n+1][i])%mod;
    printf("%d",ans);
    return 0;
}

posted @ 2019-03-29 19:30  cloud_9  阅读(78)  评论(0编辑  收藏  举报