Preface
今天射电磷(
P5++5e
)的模拟赛把大家都虐了~
最后一题个人认为质量很吼,就在这里记录一下。
题目大意
一棵
1
为根的树
1≤m≤n≤1000,1≤S≤20
题目分析
Algorithm 1
最暴力的
dp
就是设
fi,j,k
表示以
i
为根,选择了
然后Samjia直接使用启发式合并优化了这个算法,合并子树时使用类似合并果子的算法,用一个堆维护合并代价最小的两个块合并,时间复杂度应该是可以证明的。
Algorithm 2
以下是WorldWide_D标解。
令
转移方程显然:
时间复杂度 O(n3s) 。
比赛时我打了这个并且在实现时使用 dfs ,然而如果注意到 dfs 序的性质,可以将其优化。
∀DFN(i′)<DFN(i) ,显然 high(lca(i′,i)) 随着 DFN(i) 的增大而不上升。
设当前节点为 i ,我们令
再令
cntj,k−d=∑sj,k,d
显然对于一个叶子节点 fi,j,k=cntj−1,k−high(i) ,然后更新 sj,k,high(i) 以及 cnt 。
然后当退出一棵子树时,我们的 i 对原本子树中的点取
具体细节比较多,可能会有些抽象,希望读者自行思考。
时间复杂度 O(n2s) 。
代码实现
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cmath>
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int P=1000000007;
const int N=1005;
const int E=N<<1;
const int S=22;
int fa[N],high[N],last[N],next[E],tov[E],d[N],p[N],pos[N];
int f[N][S][N],cnt[S][N],sum[S][N][N];
bool vis[N];
int n,m,s,tot,l,ans;
void insert(int x,int y){tov[++tot]=y,next[tot]=last[x],last[x]=tot;}
void dfs(int x)
{
for (int i=last[x],y;i;i=next[i])
high[y=tov[i]]=high[x]+1,d[x]++,dfs(y);
}
void dp(int x)
{
if (!d[x])
{
p[++l]=x;
f[l][1][high[x]]=1;
for (int j=1;j<=s;j++)
for (int k=1;k<=n;k++)
if (k-high[x]>=0)
(f[l][j][k]+=cnt[j-1][k-high[x]])%=P;
for (int j=1;j<=s;j++)
for (int k=1;k<=n;k++)
{
if (!f[l][j][k]) continue;
(sum[j][k][high[x]]+=f[l][j][k])%=P;
if (k-high[x]>=0) (cnt[j][k-high[x]]+=f[l][j][k])%=P;
}
}
else for (int i=last[x],y;i;i=next[i]) dp(y=tov[i]);
if (x==1) return;
for (int j=1;j<=s;j++)
for (int k=1;k<=n;k++)
{
if (!sum[j][k][high[x]]) continue;
if (k-high[x]>=0) (((cnt[j][k-high[x]]-=sum[j][k][high[x]])%=P)+=P)%=P;
(sum[j][k][high[x]-1]+=sum[j][k][high[x]])%=P;
if (k-high[x]+1>=0) (cnt[j][k-high[x]+1]+=sum[j][k][high[x]])%=P;
sum[j][k][high[x]]=0;
}
}
int main()
{
freopen("tree.in","r",stdin),freopen("tree.out","w",stdout);
n=read(),m=read(),s=read();
for (int i=2;i<=n;i++) fa[i]=read(),insert(fa[i],i);
high[1]=1,dfs(1);
l=0,dp(1),ans=0;
for (int i=1;i<=l;i++)
for (int j=1;j<=s;j++)
for (int k=m;k<=n;k++)
(ans+=f[i][j][k])%=P;
printf("%d\n",ans);
fclose(stdin),fclose(stdout);
return 0;
}