hdu5103 RootedTree 状压dp,树形dp

来源:互联网 发布:加大usb电流软件 编辑:程序博客网 时间:2024/05/16 06:55

这题说的是给了n(14)个点,每个点都以他 为根的最大可容的孩子个数和最小的可溶孩子个数L[i] ,R[i]

问这n个点形成一棵树有多少种形态

我们让 dp[i][S] 表示 一 i为根节点 的 拥有孩子S(二进制数)状态的 方案数 , sub[S] , 表示 以 S 状态表示的 森林的 方案数, sum[S] 表示 一S 状态的 有根树 的 方案数

可以知道 

dp[i][S] = sub[ S^(1<<i) ] { L[i]<=|S|<=R[i]   }

sum[S] = dp[i][S] { i=0,1,2,3,,,n-1 | S&1<<i!=0  }

sub[S] = sub[S] +  sum[H]*sub[S^H]{ H 为s 的 子集 ,然后 先固定 S 中第一个不是点 不是0 的一定要在 H 中, 这样是 为了保证 不会出现一个点被算了两次,可能这个点在枚举时存在对称性 , 我们一旦确定一个点在那个位置就可以避免这种情况的出现 }

#include<iostream>#include<cstring>#include<cstdio>#include<ostream>#include<istream>#include<algorithm>#include<queue>#include<string>#include<cmath>#include<set>#include<map>#include<stack>#include<vector>#define fi first#define se second#define pii pair<int,int>#define inf (1<<30)#define eps 1e-8#define ll long longusing namespace std;const int maxn=110005;const ll mod=1000000007;int n;int d[20][2];int num[1<<14];ll sum[1<<14];ll sub[1<<14];ll dp[1<<14][15];ll Dp(int s,int c);ll Sum(int s);ll Sub(int s){    if(s==0)        return 1;    if(sub[s]!=-1)        return sub[s];    sub[s]=0;    int id[15];    int cnt=0;    for(int i=0;i<n;i++) {        if(s&(1<<i)) {            id[cnt++]=i;        }    }    for(int i=1;i<(1<<cnt);i+=2) {        int s1=0;        for(int j=0;j<cnt;j++) {            if(i&(1<<j)) {                s1^=(1<<id[j]);            }        }        sub[s]+=Sum(s1)*Sub(s^s1)%mod;        sub[s]%=mod;    }    return sub[s];}ll Dp(int s,int c){    return Sub(s^(1<<c));}ll Sum(int s){    if(sum[s]!=-1)        return sum[s];    sum[s]=0;    for(int i=0;i<n;i++) {        if(s&(1<<i)) {            if(num[s]>=d[i][0]&&num[s]<=d[i][1])                sum[s]=(sum[s]+Dp(s,i))%mod;        }    }    return sum[s];}void pre(){    memset(num,0,sizeof(num));    for(int i=0;i<(1<<14);i++) {        int o=0;        for(int j=0;j<14;j++) {            if(i&(1<<j))                o++;        }        num[i]=o;    }}int main(){    pre();    int t;    scanf("%d",&t);    while(t--) {        scanf("%d",&n);        for(int i=0;i<n;i++)            scanf("%d%d",&d[i][0],&d[i][1]);        memset(sum,-1,sizeof(sum));        memset(sub,-1,sizeof(sub));        memset(dp,-1,sizeof(dp));        printf("%I64d\n",Sum((1<<n)-1));    }    return 0;}


0 0