bzoj4013: [HNOI2015]实验比较

这个题意花里胡哨的zzzzz

其实它是个树啊。。。然后看一下范围很显然是一个树背包啦

设f[i][j]表示以i为根的子树分成j段

对于当前两个子树合并,可以合出max(j1,j2)~j1+j2范围的段数

难点就在于怎么计算方案了(是真的没有想到啊T_T)

方程是这样的:singema f[x][i]*f[y][j]*C(k,i)*C(i,j-(k-i))

后面组合数的意思是:现在我有k个位置,我先把第一棵子树的i段放在不同的k个位置,就是C(k,i),再用第二棵子树的j段把i填剩下的k-i个位置填了,那么j还有j-(k-i)个没有填,要和第一棵子树的段合并,就是i个里面选j-(k-i)个

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const LL mod=1e9+7;
LL C[110][110];
void initC(int n)
{
    C[0][0]=1;
    for(int i=1;i<=n;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;
    }
}

struct node
{
    int x,y,next;
}a[2100];int len,last[1100];
void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
int fa[1100];
int dfs(int x)
{
    int d=1;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(fa[y]==-1)
        {
            fa[y]=x;
            int s=dfs(y);
            if(s==-1)return s;
            d+=s;
        }
        else if(fa[y]!=x)return -1;
    }
    return d;
}
bool v[1100];
int n,tot[1100]; LL f[110][110],g[110];
void treeDP(int x)
{
    tot[x]=1; bool bk=false;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(v[y]==false)
        {
            v[y]=true;treeDP(y);
            if(bk==false)
            {
                bk=true;
                tot[x]+=tot[y];
                for(int i=1;i<=tot[x];i++)f[x][i]=f[y][i];
            }
            else
            {
                for(int i=1;i<=tot[x];i++)
                    for(int j=1;j<=tot[y];j++)
                        for(int k=max(i,j);k<=i+j;k++)
                            g[k]=(g[k]+f[x][i]*f[y][j]%mod*C[k][i]%mod*C[i][j-(k-i)]%mod)%mod;
                tot[x]+=tot[y];
                for(int i=1;i<=tot[x];i++)f[x][i]=g[i],g[i]=0;
            }
        }
    }
    if(bk==false)f[x][1]=1;
    else
    {
        for(int i=tot[x];i>=1;i--)f[x][i]=f[x][i-1];
    }
}


int findfa(int x)
{
    if(x==fa[x])return x;
    fa[x]=findfa(fa[x]);return fa[x];
}
struct edge{int x,y;}e[2100];int elen;
char ss[5];
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    int m,x,y;
    scanf("%d%d",&n,&m);initC(n);
    for(int i=1;i<=n+1;i++)fa[i]=i;
    elen=0;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%s%d",&x,ss+1,&y);
        if(ss[1]=='=')fa[findfa(x)]=findfa(y);
        else e[++elen].x=x,e[elen].y=y;
    }
    len=0;
    memset(last,0,sizeof(last));
    memset(v,false,sizeof(v));
    for(int i=1;i<=elen;i++)
        if(findfa(e[i].x)!=findfa(e[i].y))
            ins(findfa(e[i].x),findfa(e[i].y)),v[findfa(e[i].y)]=true;
    for(int i=1;i<=n;i++)
        if(findfa(i)==i&&v[i]==false)ins(n+1,i);
    
    int cnt=0;
    for(int i=1;i<=n+1;i++)
        if(findfa(i)==i)cnt++;
    memset(fa,-1,sizeof(fa));
    if(dfs(n+1)!=cnt)printf("0\n");
    else
    {
        memset(v,false,sizeof(v));
        treeDP(n+1);
        LL ans=0;
        for(int i=1;i<=n+1;i++)ans=(ans+f[n+1][i])%mod;
        printf("%d\n",ans);
    }
    
    return 0;
}

 

posted @ 2018-12-21 15:37  AKCqhzdy  阅读(135)  评论(0编辑  收藏  举报