BZOJ4013: [HNOI2015]实验比较(DP)

传送门

题解:
首先不等关系是一颗树, 可以直接树上DP。

有个很妙的转化是把一个序列看做对每个数分配排名。 然后记 fi,j f i , j 表示 i i 子树有j个排名的方案数, 然后问题就变为了如何合并两个排名序列。

把一个排名看做一个球,相当于有 i i 个白球j个黑球,合并为 k k 个球的方案数为(ki)(ij(ki))

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <int,int> pii;
inline int rd() {
    char ch=getchar(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
    return i*f;
}
const int N=1e2+50, mod=1e9+7;
inline int add(int x,int y) {return (x+y>=mod) ? (x+y-mod) : (x+y); }
inline int dec(int x,int y) {return (x-y<0) ? (x-y+mod) : (x-y);}
inline int mul(int x,int y) {return (LL)x*y%mod;}
inline int power(int a,int b,int rs=1) {
    for(;b;b>>=1,a=mul(a,a)) if(b&1) rs=mul(rs,a);
    return rs;
}
int n,m,C[N][N],anc[N],fa[N],vis[N],sze[N],f[N][N];
inline int ga(int x) {return (anc[x]==x) ? x : (anc[x]=ga(anc[x]));}
vector <pii> e; 
vector <int> edge[N];
inline bool findcir(int i) {
    for(int p=fa[i];p;p=fa[p]) if(p==i) return true;
    return false;
}
inline void dfs(int x) {
    static int tmp[N];
    if(!edge[x].size()) {f[x][1]=1; sze[x]=1; return;}
    for(int i=edge[x].size()-1;i>=0;i--) {
        int v=edge[x][i]; dfs(v);
        if(v==edge[x].back()) {
            for(int j=1;j<=sze[v];++j) f[x][j]=f[v][j];
        } else {
            memset(tmp,0,sizeof(tmp));
            for(int j=1;j<=sze[v];++j)
                for(int k=1;k<=sze[x];++k)
                    for(int z=max(k,j);z<=k+j;z++)
                        tmp[z]=add(tmp[z],mul(mul(f[v][j],f[x][k]),mul(C[z][j],C[j][k-(z-j)])));
            for(int j=1;j<=sze[x]+sze[v];++j) f[x][j]=tmp[j];
        }
        sze[x]+=sze[v];
    }
    ++sze[x];
    for(int i=sze[x];i;i--) f[x][i]=f[x][i-1];
}
int main() {
    n=rd(), m=rd();
    for(int i=0;i<=n;i++) 
        C[i][0]=1, anc[i]=i;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            C[i][j]=add(C[i-1][j-1],C[i-1][j]);
    for(int i=1;i<=m;i++) {
        static char ch[5];
        static int x,y;
        scanf("%d%s%d",&x,ch+1,&y);
        if(ch[1]=='<') e.push_back(pii(x,y));
        else anc[ga(x)]=ga(y);
    }
    for(int i=0;i<e.size();++i) {
        pii &u=e[i];
        fa[ga(u.second)]=ga(u.first);
    }
    for(int i=1;i<=n;i++) 
        if(ga(i)==i && findcir(i))  
             return puts("0"),0;
    for(int i=1;i<=n;i++)
        if(ga(i)==i) edge[fa[i]].push_back(i);
    dfs(0); int ans=0;
    for(int i=1;i<=sze[0];++i) ans=add(ans,f[0][i]);
    cout<<ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值