[JZOJ]兔兔和蛋蛋

来源:互联网 发布:怎样挑选淘宝供应商 编辑:程序博客网 时间:2024/04/28 11:04

题目大意

以1为根的树有n个点,每个点i有个贡献值为a[i],还可以分配一个b[i],其中b[i][0,m]。对于每一对(i,j),若j在i子树中,且b[i]>b[j],则对该局面贡献a[i]。
现在问贡献值为0~K的分配方案分别有多少,模1e9+7。
这里写图片描述
注:开O2

分析

首先40分直接98再加爆掉K的剪枝就可以过了。
分析一下m从14跨越到1e8怎么做?那我们肯定只用知道我们的方案的b[i]的种类数,然后组合数就可以了嘛。
n=14,也不可折半,那么应该是个状压DP。直接按DFN做?没用啊。
很机智地,我们从小到大填b[i]。这里的b[i]是离散后的。
那么设f[i][j][s]为,填到i,当前贡献为j,n个点的填数与否状态为s,的方案数。
考虑转移,直接暴力枚举填i+1的点有哪些相当于枚举子集,这样是O(nk3n)的,萎成了40分。所以应该要每次只填一个。
那么这时考虑按dfn来做,发现每次只需要填一个数,不管是填i+1还是填i,都没有影响。这样就得出了新的算法:s的状态顺序为dfn,转移的话,对于一个f[i][j][s],我们可以填i,即同层转移,也可以填i+1,转移到新的一层。这样子时间复杂度是O(nk2nn)的。就可以过了,注意转移算新状态要O(1)算,即预处理一些东西。
我打的时候想复杂了,常数巨大,枚举了许多无用状态,当然复杂度还是一样的嘛····

代码

#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;#define fo(i,j,k) for(i=j;i<=k;i++)#define fd(i,j,k) for(i=j;i>=k;i--)typedef long long ll;typedef double db;const int N=17000;const int mo=1e9+7;int lim,i,j,k,l,s,ans,kx,n,m,K,ct;int cnt[N],av[N][25],And[25][25],a[25];int f[17][22][N],g[17][22][N],rev[25],fac[25];int tt,b[25],next[25],first[25],dfn[25],ref[25],siz[25],t1;void cr(int x,int y){    tt++;    b[tt]=y;    next[tt]=first[x];    first[x]=tt;}void dfs(int x){    dfn[x]=++t1;    siz[x]=1;    ref[t1]=x;    for(int p=first[x];p;p=next[p])     {        dfs(b[p]);        siz[x]+=siz[b[p]];    }}int ksm(int x,int y){    int ret=1;    while (y)    {        if (y&1) ret=1ll*ret*x%mo;        y>>=1;        x=1ll*x*x%mo;    }    return ret;}void predo(){    fo(i,0,lim)        fo(j,0,n-1)             if (i&(1<<j))                 cnt[i]++;            else                av[i][++av[i][0]]=j+1;    fo(i,0,n-1)        fo(j,i,n-1)            And[i][j]=And[i][j-1]+(1<<j);    fac[0]=1;    fo(i,1,n) fac[i]=1ll*fac[i-1]*i%mo;    fo(i,0,n) rev[i]=ksm(fac[i],mo-2);}void trans(int i){    int j,k,l,s,x,tmp;    fo(j,0,K)        fo(s,0,lim)            if (f[i][j][s])            {                fo(k,1,av[s][0])                {                    ct++;                    x=av[s][k];                    tmp=a[ref[x]]*cnt[And[x-1][x+siz[ref[x]]-1-1]&s];                    if (j+tmp<=K)                    (g[x][j+tmp][s+(1<<(x-1))]+=f[i][j][s])%=mo;                }            }    fo(k,1,n)        fo(j,0,K)            fo(s,0,lim)                if (g[k][j][s])                {                    for(l=1;l<=av[s][0]&&av[s][l]<=k;l++);                    fo(l,l,av[s][0])                    {                        ct++;                        x=av[s][l];                        tmp=a[ref[x]]*cnt[And[x-1][x+siz[ref[x]]-1-1]&s];                        if (j+tmp<=K)                        (g[x][j+tmp][s+(1<<(x-1))]+=g[k][j][s])%=mo;                    }                }}int c(int n,int m){    int ret=1,l;    fd(l,m,m-n+1) ret=1ll*ret*l%mo;    return 1ll*ret*rev[n]%mo;}int main(){    scanf("%d %d %d\n",&n,&m,&K);    m++;    fo(i,1,n) scanf("%d",a+i);    fo(i,2,n)    {        scanf("%d",&j);        cr(j,i);    }    dfs(1);    f[0][0][0]=1;    lim=(1<<n)-1;    predo();    fo(i,0,min(n,m)-1)    {        trans(i);        fo(k,1,n)            fo(j,0,K)                fo(s,0,lim)                {                    f[i+1][j][s]=(f[i+1][j][s]+g[k][j][s])%mo;                    g[k][j][s]=0;                }    }    fo(k,0,K)    {        ans=0;        fo(i,1,min(n,m))        {            kx=c(i,m);            ans=(1ll*ans+1ll*kx*f[i][k][lim])%mo;        }        printf("%d\n",ans);    }}
0 0
原创粉丝点击