【BZOJ 4013】[HNOI2015]实验比较

来源:互联网 发布:软件进口代理 编辑:程序博客网 时间:2024/06/06 19:03

题目描述

BZOJ4013—Portal

题目解析

首先我们把每个数看做一个点,不妨将等号连接的两个点缩点,然后剩下的约束关系便会形成一棵树或森林(至于为什么是一棵树请看题目—哭,这是道语文题吧,写那么小),我们要求的合法序列中的等号两端也可以缩成一个块,因为等号两端无论怎么交换也不会产生新的方案,那么现在题目就变成了对于一棵树,求有多少种排列方案保证父节点在序列中排在子节点前。对于一颗子树,我们设以x为跟的子树划分成i块的方案数为dpx,i;那么对于x的儿子合并就相当于将两个序列合并,即合并dpa,jdpb,k,那么我们可以想像把两种颜色的“块”在保证相对位置的情况下被分进i个新的块里,即黑球j个,白球k个,放进i个盒子里,很明显,当一个盒子中同时放入两个黑色的球时,他就相当于黑球只有j1个了,方案数重复,故相同颜色的球只能放入不同盒子中,我们就可以先放入黑球,方案数C(j,i),在放入白球,方案数C(k(ij),j),很明显i(max(j,k),j+k),

dpx,i=dpa,j×dpb,k×C(j,i)×C(k(ij),j)
。那么,将所有子树合并后有dpx,i,又因为x节点也有一个“块”,而且其必须放在其他“块”之前,所以有dpx,i=dpx,i1。算法总时间复杂度O(n4),当然,枚举时保留当前节点的sz可以大大减少枚举数量。

代码

/**************************************************************    Problem: 4013    User: bzjudge2    Language: C++    Result: Accepted    Time:36 ms    Memory:1440 kb****************************************************************/#include<iostream>#include<cstring>#include<cstdio>#include<cmath>#include<algorithm>#include<cstdlib>using namespace std;#define MAXN 100#define MAXM 100#define INF 0x3f3f3f3ftypedef long long int LL;const int MOD = 1e9+7;template<class T>void Read(T &x){    x=0;char c=getchar();bool flag=0;    while(c<'0'||'9'<c){if(c=='-')flag=1;c=getchar();}    while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}    if(flag)x=-x;}struct node{    int v;    node *nxt;}*adj[MAXN+10],Edges[MAXM*2+10],*New=Edges;void addedge(int u,int v){    node *p=++New;    p->v=v;    p->nxt=adj[u];    adj[u]=p;}int n,m;int u[MAXM+10],v[MAXM+10];int d[MAXN+10];int rt[MAXN+10];int root(int x){return rt[x]=(rt[x]==x?x:root(rt[x]));}int C[MAXN+10][MAXN+10];void init(){    C[0][0]=1;    for(int i=1;i<=n;++i){        C[i][0]=C[i][i]=1;        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)rt[i]=i;}LL dp[MAXN+10][MAXN+10];LL g[MAXN+10];int sz[MAXN+10];bool vis[MAXN+10];bool dfs(int x){    vis[x]=true;    bool fir=true;    for(node *p=adj[x];p!=NULL;p=p->nxt){        if(vis[p->v])return false;        if(!dfs(p->v))return false;        if(fir){            sz[x]=sz[p->v],fir=false;            for(int i=1;i<=sz[x];++i)dp[x][i]=dp[p->v][i];        }        else{            memset(g,0,sizeof(g));            for(int j=1;j<=sz[x];++j)                for(int k=1;k<=sz[p->v];++k)if(dp[x][j]&&dp[p->v][k])                    for(int i=max(j,k);i<=j+k;++i)                        g[i]=(g[i]+dp[x][j]*dp[p->v][k]%MOD*C[i][j]%MOD*C[j][k-(i-j)]%MOD)%MOD;            sz[x]+=sz[p->v];            for(int i=1;i<=sz[x];++i)dp[x][i]=g[i];        }    }    if(x){        ++sz[x];        if(!fir)for(int i=sz[x];i>=1;--i)dp[x][i]=dp[x][i-1];        else dp[x][1]=1;    }    return true;}int main(){    Read(n),Read(m);    init();    int a,b;    char str[3];    int cnt=0;    for(int i=1;i<=m;++i){        scanf("%d%s%d",&a,str,&b);        if(str[0]=='=')rt[root(a)]=root(b);        else u[++cnt]=a,v[cnt]=b;    }    for(int i=1;i<=cnt;++i){        a=root(u[i]),b=root(v[i]);        if(a==b){            puts("0");            return 0;        }        else addedge(a,b),++d[b];    }    for(int i=1;i<=n;++i){        a=root(i);        if(!d[a])addedge(0,a),++d[a];    }    if(!dfs(0)){        puts("0");        return 0;    }    else{        LL ans=0;        for(int i=1;i<=sz[0];++i)            ans=(ans+dp[0][i])%MOD;        printf("%lld\n",ans);    }}
0 0
原创粉丝点击