题目大意
以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(n∗k∗3n)
的,萎成了40分。所以应该要每次只填一个。
那么这时考虑按dfn来做,发现每次只需要填一个数,不管是填i+1还是填i,都没有影响。这样就得出了新的算法:s的状态顺序为dfn,转移的话,对于一个f[i][j][s],我们可以填i,即同层转移,也可以填i+1,转移到新的一层。这样子时间复杂度是
O(n∗k∗2n∗n)
的。就可以过了,注意转移算新状态要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);
}
}