给定一棵 个点的以 为根的有根树,点 有权值 和 。定义树合法当且仅当每个点 都满足其子树内 的 为 。
有些 表示点 没有限制,还有些 表示 可以在 中任选。求有多少种给所有 的 赋值的方案使得树是合法的,对 取模。
,。
首先设 表示 子树内 的最大值,则若 且 那么一定无解。
刚开始设的状态是 表示 的子树,确定 的 都填完了,是否有 的 , 的还未确定的 有 个的方案数,可是这样由于有容斥所以我不会优化,只能做到 。
考虑设 表示 子树内 是否确定出现,则一个数 确定出现当且仅当子树内有某个 或 。
设 表示 的子树,( 中)确定出现过的数都填完了,是否有 的数,共填了 种未确定出现过的数,且这 种数的大小顺序还未确定的方案数。
考虑合并子树的过程,首先对于 的数 , 子树中可能有一种未确定出现过的数为 ,所以滚一次背包;对于 的数同理。
接下来要处理两颗子树中都未出现过,但相同的数,也是直接滚背包,即看一下 子树中未确定出现的数是否有一种和 子树中第 种未确定的数相同。
做完这些处理之后直接卷积即可,注意滚背包的时候要乘系数,即从 种数中选一种出来配对,系数为 。
最后再把 合并一下。
但是考虑已经合并进 的某个儿子 ,在合并新的儿子 时, 这个数比较特殊,即使 也不能有 子树中未确定出现过的数“雪藏”在这里,而我们的状态中并未记录相关信息。所以需要将儿子按照 从大到小的顺序合并。
最后若 则要将 推上去,即选一些未确定的数出来填了。
时间复杂度就是树上背包的复杂度,即 。
代码如下:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int S=5005;
#define p 1000000007
int fra[S],C[S][S];
int n,a[S],b[S],fa[S];
bool vis[S][S];
vector<int> son[S];
int tu[S],tv[S],tmp[S],h[2][S],th[2][S];
int siz[S],f[S][2][S];
inline void add(int &x,int y)
{
x+=y;
if(x>=p) x-=p;
}
int main()
{
for(int i=0;i<=S-3;i++)
{
fra[i]=(i==0?1:1ll*fra[i-1]*i%p);
C[i][0]=1;
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
}
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
fa[1]=0;
for(int i=2;i<=n;i++) cin>>fa[i],son[fa[i]].push_back(i);
for(int u=n;u>=1;u--)
{
sort(son[u].begin(),son[u].end(),[&](int x,int y){return b[x]>b[y];});
int lb=-1;
for(int v:son[u]) lb=max(lb,b[v]);
siz[u]=1;
memset(h,0,sizeof(h));
if(a[u]!=-1) vis[u][a[u]]=true,h[a[u]==lb][0]=1;
else
{
h[0][1]=1;
if(lb!=-1) h[1][0]=1;
}
for(int v:son[u])
{
auto doit=[&](int id,int vt,bool has){
memcpy(tu,h[id],sizeof(h[id]));
memset(tv,0,sizeof(tv));
if(vt&1) for(int i=0;i<=siz[v];i++) add(tv[i],f[v][0][i]);
if(vt&2) for(int i=0;i<=siz[v];i++) add(tv[i],f[v][1][i]);
memset(tmp,0,sizeof(tmp));
if(lb!=-1&&!has&&vis[v][lb]) return;
for(int i=0;i<=n;i++)
if(i==lb)
{
if(vt==3&&has&&!vis[v][i])
for(int j=1;j<=siz[v];j++) tv[j-1]=1ll*tv[j]*j%p,tv[j]=0;
}
else
{
if(vis[u][i]==vis[v][i]) continue;
if(!vis[u][i])
for(int j=1;j<=siz[u];j++) add(tu[j-1],1ll*tu[j]*j%p);
if(!vis[v][i]&&i!=b[v])
for(int j=1;j<=siz[v];j++) add(tv[j-1],1ll*tv[j]*j%p);
}
for(int i=0;i<=siz[u];i++)
{
for(int j=0;j<=siz[v];j++)
add(tmp[i+j],1ll*tu[i]*tv[j]%p);
for(int j=1;j<=siz[v];j++)
add(tv[j-1],1ll*tv[j]*j%p);
}
};
memset(th,0,sizeof(th));
if(lb==-1||b[v]!=lb)
{
if(lb==-1||!vis[u][lb])
{// 0->0
doit(0,3,false);
for(int i=0;i<=siz[u]+siz[v];i++) add(th[0][i],tmp[i]);
}
if(lb!=-1)
{// 0->1
doit(0,3,true);
for(int i=0;i<=siz[u]+siz[v];i++) add(th[1][i],tmp[i]);
}
{// 1->1
doit(1,3,false);
for(int i=0;i<=siz[u]+siz[v];i++) add(th[1][i],tmp[i]);
doit(1,3,true);
for(int i=0;i<=siz[u]+siz[v];i++) add(th[1][i],tmp[i]);
}
}
else
{
if(lb==-1||!vis[u][lb])
{// 0->0
doit(0,1,false);
for(int i=0;i<=siz[u]+siz[v];i++) add(th[0][i],tmp[i]);
}
if(lb!=-1)
{// 0->1
doit(0,2,true);
for(int i=0;i<=siz[u]+siz[v];i++) add(th[1][i],tmp[i]);
}
{// 1->1
doit(1,1,false);
for(int i=0;i<=siz[u]+siz[v];i++) add(th[1][i],tmp[i]);
doit(1,2,true);
for(int i=0;i<=siz[u]+siz[v];i++) add(th[1][i],tmp[i]);
}
}
memcpy(h,th,sizeof(h));
for(int i=0;i<=n;i++) vis[u][i]|=vis[v][i];
siz[u]+=siz[v];
}
if(b[u]==lb)
for(int i=0;i<=siz[u];i++) f[u][0][i]=h[0][i];
else if(b[u]==-1)
for(int i=0;i<=siz[u];i++)
add(f[u][0][i],h[0][i]),
add(f[u][1][i],h[1][i]);
else
{
int cnt=0;
for(int i=lb+1;i<b[u];i++) cnt+=!vis[u][i];
if(vis[u][lb])
for(int i=cnt;i<=siz[u];i++)
add(f[u][0][i-cnt],1ll*h[0][i]*C[i][cnt]%p*fra[cnt]%p),
add(f[u][0][i-cnt],1ll*h[1][i]*C[i][cnt]%p*fra[cnt]%p);
else
for(int i=cnt;i<=siz[u];i++)
add(f[u][0][i-cnt],1ll*h[lb!=-1][i]*C[i][cnt]%p*fra[cnt]%p);
}
if(b[u]!=-1&&vis[u][b[u]]) return cout<<"0\n",0;
b[u]=max(b[u],lb);
for(int i=0;i<b[u];i++) vis[u][i]=true;
}
int cnt=n-b[1];
for(int i=b[1]+1;i<=n;i++) cnt-=vis[1][i];
int ans=0;
for(int i=0;i<=siz[1];i++)
add(ans,1ll*(f[1][0][i]+f[1][1][i])*C[cnt][i]%p*fra[i]%p);
cout<<ans<<'\n';
return 0;
}
/*
vis[u][i]=1 且 vis[v][i]=0 的怎么合并
f[u][i] 表示 u 子树,填完了必定出现的值,有 i 种未确定的未出现的值
- que1:这 i 种值顺序确定好了吗
没确定好
*/