D1T1
洛谷题目传送门
题目描述
给定一个n个点m条边的无向图,每条边有高度和长度,Q次询问,每次给定起点,以及限制高度,求从起点能通过高度大于限制高度的边到达的点中,到1号点最短路的最小值
强制在线
65pts 不强制在线
把边权和询问的权值都排序,用并查集维护连通块内到1号点距离最小的点
100pts
解法一
在65pts上改进
我们只需要把用并查集合并的过程可持久化,就能访问任何一个版本的并查集
就可以做到在线了
若果使用按秩合并的并查集
O
(
n
l
o
g
2
n
)
O(nlog^2n)
O(nlog2n)
解法二
建出Kruskal重构树
那么可以到达的点就是一个子树,用树上倍增维护最远能走到那个点
预处理每个点的子树内到1号店距离最近的点即可
时间复杂度都是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),且细节比较少,空间消耗小
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5+3;
struct P
{
int id;
LL v;
};
bool operator <(const P &x,const P &y)
{
return x.v>y.v;
}
priority_queue<P> q;
struct node
{
int y,next,l;
}e[4*N];
struct edge
{
int x,y,h;
}E[2*N];
int link[N],t;
int n,m,Q,K,s;
void Insert(int x,int y,int l)
{
e[++t].y=y;
e[t].l=l;
e[t].next=link[x];
link[x]=t;
}
bool cmp(edge a,edge b)
{
return a.h>b.h;
}
int read(){
int ans=0,op=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-') op=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){ans=(ans<<1)+(ans<<3)+ch-'0';ch=getchar();}
return ans*op;
}
bool vis[N];
LL dis[N];
P Pc(int x,LL v)
{
P tem;
tem.id=x;
tem.v=v;
return tem;
}
void dijistra()
{
for(int i=1;i<=n;i++)
{
vis[i]=0;
dis[i]=1e15;
}
dis[1]=0;
q.push(Pc(1,0));
while(!q.empty())
{
P tp=q.top();q.pop();
int x=tp.id;
if(vis[x]) continue;
vis[x]=1;
for(int i=link[x];i;i=e[i].next)
{
int y=e[i].y;
// cout<<x<<' '<<y<<' '<<dis[x]<<' '<<e[i].l<<' '<<dis[y]<<endl;
if(dis[x]+e[i].l<dis[y])
{
dis[y]=dis[x]+e[i].l;
q.push(Pc(y,dis[y]));
}
}
}
}
int cnt,f[2*N],val[2*N],Min[2*N];
int Find(int x)
{
if(x==f[x]) return x;
return f[x]=Find(f[x]);
}
int up[2*N][30];
void kruskal()
{
for(int i=1;i<=n;i++)
{
Min[i]=dis[i];
f[i]=i;
}
sort(E+1,E+m+1,cmp);
memset(link,0,sizeof(link));
t=0;
for(int i=1;i<=m;i++)
{
int x=E[i].x;
int y=E[i].y;
int fx=Find(x),fy=Find(y);
if(fx==fy) continue;
cnt++;
val[cnt]=E[i].h;
Min[cnt]=min(Min[fx],Min[fy]);
f[fx]=cnt;f[cnt]=cnt;f[fy]=cnt;
up[fx][0]=cnt;
up[fy][0]=cnt;
Insert(cnt,fx,0);
Insert(cnt,fy,0);
}
}
int main()
{
freopen("return.in","r",stdin);
freopen("return.out","w",stdout);
int T;
cin>>T;
while(T--)
{
t=0;
memset(link,0,sizeof(link));
memset(vis,0,sizeof(vis));
memset(Min,89,sizeof(Min));
memset(up,0,sizeof(up));
n=read();m=read(); cnt=n;
for(int i=1;i<=m;i++)
{
int x,y,l,h;
x=read();
y=read();
l=read();
h=read();
Insert(x,y,l);
Insert(y,x,l);
E[i].x=x;
E[i].y=y;
E[i].h=h;
}
dijistra();
kruskal();
for(int k=1;(1<<k)<=cnt;k++)
for(int i=1;i<=cnt;i++)
up[i][k]=up[up[i][k-1]][k-1];
Q=read();K=read();s=read();
LL last=0;
while(Q--)
{
int v0,h0;
v0=read();
h0=read();
int v=(v0+K*last-1)%n+1;
int h=(h0+K*last)%(s+1);
for(int k=22;k>=0;k--)
if(up[v][k]&&val[up[v][k]]>h)
v=up[v][k];
last=Min[v];
printf("%lld\n",Min[v]);
}
}
return 0;
}
附赠Kruskal重构树三道题
BZOJ3545Peaks
CF1408G
[IOI2018] werewolf 狼人
D1T2
洛谷题目传送门
8pts
n!枚举排列,然后判断即可
44pts
首先考虑什么样的序列是符合条件的
题目提示启发我们,可以考虑每个元素的移动
因为总交换次数是要达到下界,所以每一次交换都必须是有益的
考虑如果排列第i个位置是p[i]
若
p
[
i
]
=
i
p[i]=i
p[i]=i,则这位置不能被交换
若
p
[
i
]
<
i
p[i]<i
p[i]<i,则这个数需要往左移动,且不能向右移动
那么不能存在一个j满足,
j
>
i
,
p
[
j
]
<
p
[
i
]
j>i,p[j]<p[i]
j>i,p[j]<p[i],否则这两个位置一定会交换一次,那么
p
[
i
]
p[i]
p[i]就向相反方向移动了,答案一定会变大,即
i
i
i右边的数都比
p
[
i
]
p[i]
p[i]大
若
p
[
i
]
>
i
p[i]>i
p[i]>i,类似的可以得到i左边的数都比
p
[
i
]
p[i]
p[i]小
且这三个限制和原限制是等价的
直接状压选了那些数字即可
字典序的限制可以类似数位dp去搞
复杂度
O
(
2
n
n
)
O(2^nn)
O(2nn)
#include<bits/stdc++.h>
using namespace std;
const int N = 6e5+7;
typedef long long LL;
const int mod = 998244353;
int n,a[N];
LL f[1<<20][2];
void solve()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int m=(1<<n);
memset(f,0,sizeof(f));
f[0][1]=1;
for(int s=0;s<m-1;s++)
{
int p=0;
int a1=0,a2=n+1;
for(int i=1;i<=n;i++)
if((s>>(i-1))&1)
{
p++;
a1=max(a1,i);
}
else a2=min(a2,i);
p++;
for(int limit=0;limit<=1;limit++)
{
int up=(limit?a[p]:1);
for(int i=up;i<=n;i++)
{
int k=(limit&&(i==up));
if(!((s>>(i-1))&1))
{
if(i>=p)
{
if(a1<i)
f[s+(1<<(i-1))][k]=(f[s+(1<<(i-1))][k]+f[s][limit])%mod;
}
else
{
if(i<=a2)
f[s+(1<<(i-1))][k]=(f[s+(1<<(i-1))][k]+f[s][limit])%mod;
}
}
}
}
}
printf("%lld\n",f[m-1][0]);
}
int main()
{
int T;
cin>>T;
while(T--)
{
solve();
}
return 0;
}
80pts
一个重要的结论:
不考虑字典序限制
题目所求等价于求有多少个排列,满足排列中不存在长度大于等于3的下降子序列
必要性:
即若该排列合法,则不存在长度大于等于3的下降子序列
若不然,则存在三个数
i
,
j
,
k
i,j,k
i,j,k满足,
i
<
j
<
k
,
p
[
i
]
>
p
[
j
]
>
p
[
k
]
i<j<k,p[i]>p[j]>p[k]
i<j<k,p[i]>p[j]>p[k]
若
j
<
p
[
j
]
j<p[j]
j<p[j],由上文可知,j左边的数都比j小,与
p
[
i
]
>
p
[
i
]
p[i]>p[i]
p[i]>p[i]矛盾
若
j
>
p
[
j
]
j>p[j]
j>p[j],则j右边的数都比j大,与
p
[
j
]
>
p
[
k
]
p[j]>p[k]
p[j]>p[k]矛盾
所以矛盾,即原命题成立
充分性
即若不存在
p
[
i
]
>
p
[
j
]
>
p
[
k
]
p[i]>p[j]>p[k]
p[i]>p[j]>p[k],则性质成立
考虑一次交换,交换了
p
[
i
]
,
p
[
i
+
1
]
p[i],p[i+1]
p[i],p[i+1]
则由题可知,
p
[
i
]
>
p
[
i
+
1
]
p[i]>p[i+1]
p[i]>p[i+1]
又因为没有长度大于等于3的下降子序列,所以i左边的数都小于
p
[
i
]
p[i]
p[i]
那么就有
p
[
1
…
…
i
−
1
]
p[1……i-1]
p[1……i−1]和
p
[
i
+
1
]
p[i+1]
p[i+1]总共i个数比
p
[
i
]
p[i]
p[i]小,即
p
[
i
]
>
i
p[i]>i
p[i]>i
类似的我们可以得到
p
[
i
+
1
]
<
i
+
1
p[i+1]<i+1
p[i+1]<i+1,那么这次交换对最终排列而言是合法的
所以命题成立
即题目等价于求有多少的排列,满足排列中不存在长度大于等于3的下降子序列
考虑dp
设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示有多少个长度为i的排列,满足第一个元素是j,且不存在长度大于等于3的下降子序列,则由定义知
j
≤
i
j\leq i
j≤i
考虑第二个数填k
若
j
<
=
k
j<=k
j<=k,则加上dp[i-1][k],这里之所以能等于j,而不会重复,是因为虽然整个序列是一个排列,但是每一个子段并不是一个排列,而我们之所以能转移,是因为我们只考虑元素的相对关系,即离散化之后的值,那么长度为i的数列,长度为i-1的数列,离散化之后有值相等是显然可以的
若
1
<
k
<
j
1<k<j
1<k<j,那么一定会存在一个
j
,
k
,
1
j,k,1
j,k,1的不合法,下降子序列,因此贡献为0
若
k
=
=
1
k==1
k==1
贡献显然并不是
d
p
[
i
−
1
]
[
1
]
dp[i-1][1]
dp[i−1][1]
首先1~j-1一定是升序排列的,否则就会存在不合法的下降子序列
若不合法,则存在
x
<
y
,
j
>
p
[
x
]
>
p
[
y
]
>
1
x<y,j>p[x]>p[y]>1
x<y,j>p[x]>p[y]>1
考虑一个映射:
把x移到x+1的位置上,特别的让j-1移到1的位置上
则新序列和原序列是一一对应的,即是一个双双射
且新数列中存在
(
j
−
1
,
p
[
x
]
−
1
,
p
[
y
]
−
1
)
(j-1,p[x]-1,p[y]-1)
(j−1,p[x]−1,p[y]−1)的下降子序列
所以若填的k等于1,则等价于不存在以j-1开始的长度为3的下降子序列,即dp[i-1][j-1]
综合三种情况
d
p
[
i
]
[
j
]
=
∑
k
=
j
−
1
i
−
1
d
p
[
i
−
1
]
[
k
]
dp[i][j]=\sum_{k=j-1}^{i-1}dp[i-1][k]
dp[i][j]=∑k=j−1i−1dp[i−1][k]
考虑字典序的限制
枚举与给定排列的最长公共前缀i
设前缀最大值为x,满足
p
[
j
]
<
=
x
,
j
>
=
i
p[j]<=x,j>=i
p[j]<=x,j>=i的j的个数为s
那么离散化之后,x就是s
又因为有字典序的限制,所以填的数字应该>p[i],也就是说,当前位置可以填的最小的数是s+1,最大的是n-i+1,当然是离散化之后的
即答案是
A
n
s
=
∑
i
=
1
n
−
1
∑
k
=
s
+
1
n
−
i
+
1
d
p
[
n
−
i
+
1
]
[
k
]
Ans=\sum_{i=1}^{n-1}\sum_{k=s+1}^{n-i+1}dp[n-i+1][k]
Ans=i=1∑n−1k=s+1∑n−i+1dp[n−i+1][k]
预处理dp值,通过前缀和优化,复杂度
O
(
n
2
)
O(n^2)
O(n2)
因为代码很简单,所以略
100pts
观察到答案实际上若干dp数组的后缀和
设
s
(
n
,
m
)
=
∑
i
=
m
n
d
p
[
n
]
[
i
]
s(n,m)=\sum_{i=m}^ndp[n][i]
s(n,m)=∑i=mndp[n][i],其中
m
≤
n
m\leq n
m≤n
则
s
(
n
,
m
)
=
∑
i
=
m
+
1
n
d
p
[
n
]
[
i
]
+
d
p
[
n
]
[
m
]
s(n,m)=\sum_{i=m+1}^ndp[n][i]+dp[n][m]
s(n,m)=i=m+1∑ndp[n][i]+dp[n][m]
=
∑
i
=
m
+
1
n
d
p
[
n
]
[
i
]
+
∑
i
=
m
−
1
n
−
1
d
p
[
n
−
1
]
[
i
]
=\sum_{i=m+1}^ndp[n][i]+\sum_{i=m-1}^{n-1}dp[n-1][i]
=i=m+1∑ndp[n][i]+i=m−1∑n−1dp[n−1][i]
=
s
(
n
,
m
+
1
)
+
s
(
n
−
1
,
m
−
1
)
=s(n,m+1)+s(n-1,m-1)
=s(n,m+1)+s(n−1,m−1)
考虑组合意义,
在平面直角坐标系上从
(
0
,
0
)
(0,0)
(0,0)走到
(
n
,
m
)
(n,m)
(n,m)每次向下走一步或者向右上走一步的方案数
并且不能超过
y
=
x
y=x
y=x这条线和x轴之下
首先仅通过这两种走法,随便走,不可能越过
y
=
x
y=x
y=x,但有可能越过x轴
因为只有第二种会向右走一步,所以一共有n次第二种操作,也可以得到有n-m次第一种操作
为了让这两种操作相似,不妨把想下,改为向右
则原先走到(n,m),新的操作就会走到(2n-m,m),因为多向右走了n-m次
即,每次向右上或者右下走一步,走到(2n-m,m)的方案数,且不能超过x轴
这和原问题也是双射
因为不能超过x轴,所以每个前缀里,向上走的次数要大于等于向下走的次数
这就是卡特兰数的变种问题,方案数为
s
(
n
,
m
)
=
C
2
n
−
m
n
−
m
−
C
2
n
−
m
n
−
m
−
1
s(n,m)=C_{2n-m}^{n-m}-C_{2n-m}^{n-m-1}
s(n,m)=C2n−mn−m−C2n−mn−m−1
A
n
s
=
∑
i
=
1
n
−
1
s
(
n
−
i
+
1
,
s
+
1
)
Ans=\sum_{i=1}^{n-1}s(n-i+1,s+1)
Ans=i=1∑n−1s(n−i+1,s+1)
预处理组合数,用树状数组求s
复杂度
O
(
n
log
n
)
O(n\log n)
O(nlogn)
#include<bits/stdc++.h>
using namespace std;
const int N = 6e5+7;
typedef long long LL;
const int mod = 998244353;
int c[N];
int n;
void add(int x,int v)
{
for(int i=x;i<=n;i+=i&-i)
c[i]+=v;
}
int ask(int x)
{
int res=0;
for(int i=x;i;i-=i&-i)
res+=c[i];
return res;
}
int a[N];
LL fac[N*2],inv[N*2];
LL M = N*2-2;
LL Pow(LL a,LL b)
{
LL res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int pre[N],suf[N];
int leq[N];
LL C(int n,int m)
{
if(n<m) return 0;
return (LL)fac[n]*inv[m]%mod*inv[n-m]%mod;
}
LL S(int n,int m)
{
return (C(2*n-m,n-m)-C(2*n-m,n-m-1)+mod)%mod;
}
void solve()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
pre[i]=max(pre[i-1],a[i]);
suf[n+1]=n+1;
for(int i=n;i>=1;i--)
suf[i]=min(suf[i+1],a[i]);
for(int i=n;i>=1;i--)
{
add(a[i],1);
leq[i]=ask(pre[i]);
}
for(int i=1;i<=n;i++)
add(a[i],-1);
int Max=0;
LL ans=0;
for(int i=1;i<=n;i++)
{
if(Max>suf[i]) break;
if(leq[i]+1<=n-i+1)
ans=(ans+S(n-i+1,leq[i]+1))%mod;
if(a[i]<pre[i]) Max=max(Max,a[i]);
}
printf("%lld\n",ans);
}
int main()
{
int T;
cin>>T;
fac[0]=1;
for(int i=1;i<=M;i++)
fac[i]=(LL)fac[i-1]*i%mod;
inv[M]=Pow(fac[M],mod-2);
for(int i=M-1;i>=0;i--)
inv[i]=(LL)inv[i+1]*(i+1)%mod;
while(T--)
{
solve();
}
return 0;
}
D2T1
首先使用muiltiset可以方便的维护出每个龙使用的是哪一把剑
因为题目要求x恰好让血量变为0,多了少了都不行
所以
等价于求方程组
{
c
1
x
≡
a
1
(
m
o
d
p
1
)
c
2
x
≡
a
2
(
m
o
d
p
2
)
…
…
c
n
x
≡
a
n
(
m
o
d
p
n
)
\left\{ \begin{matrix} c_1x\equiv a_1(mod\;p_1) \\ c_2x\equiv a_2(mod\;p_2) \\ ……\\ c_nx\equiv a_n(mod\;p_n) \end{matrix} \right.
⎩⎪⎪⎨⎪⎪⎧c1x≡a1(modp1)c2x≡a2(modp2)……cnx≡an(modpn)
的通解x
因为模数不互质,所以选择exCRT求解
但时x有系数,考虑化为不带系数
c
i
x
≡
a
i
(
m
o
d
p
i
)
c_ix\equiv a_i(mod\;p_i)
cix≡ai(modpi)
等价于
c
i
x
+
p
i
y
=
a
i
c_ix+p_iy=a_i
cix+piy=ai
求出一个特解
x
0
x_0
x0
那么
x
x
x的通解可化为
X
=
x
0
+
k
×
p
i
g
c
d
(
p
i
,
c
i
)
X=x0+k\times\frac{p_i}{gcd(p_i,c_i)}
X=x0+k×gcd(pi,ci)pi
同时模
p
i
g
c
d
(
p
i
,
c
i
)
\frac{p_i}{gcd(p_i,c_i)}
gcd(pi,ci)pi
即可得到
X
≡
x
0
(
m
o
d
p
i
g
c
d
(
p
i
,
c
i
)
)
X\equiv x0(mod\;\frac{p_i}{gcd(p_i,c_i)})
X≡x0(modgcd(pi,ci)pi)
这就化成了一般形式
使用exCRT求解即可
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5+7;
LL gcd(LL a,LL b)
{
if(!b) return a;
return gcd(b,a%b);
}
LL lcm(LL a,LL b)
{
return a/gcd(a,b)*b;
}
LL exgcd(LL a,LL b,LL&x,LL &y)
{
if(!b)
{
x=1;
y=0;
return a;
}
LL d=exgcd(b,a%b,x,y);
LL z=x;
x=y;
y=z-(a/b)*y;
return d;
}
LL mul(LL a,LL b,LL mod)
{
LL res=0;
while(b)
{
if(b&1) res=(res+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return res;
}
LL Ans(LL a,LL b,LL m)
{
LL x,y;
LL d=exgcd(a,m,x,y);
if(b%d!=0) return -1;
LL t=m/d;
x=mul(x,b/d,t);
return (x%t+t)%t;
}
LL c[N],a[N],p[N];
LL ceil(LL a,LL b)
{
if(a%b==0) return a/b;
return a/b+1;
}
LL search(LL a,LL b,LL c)
{
if(a>=b) return a;
return a+1ll*ceil(b-a,c)*c;
}
LL EXCRT(int n,LL Base)
{
LL x=Ans(c[1],a[1],p[1]),m=p[1]/gcd(p[1],c[1]);
if(x==-1) return -1;
for(int i=2;i<=n;i++)
{
LL t=Ans(mul(c[i],m,p[i]),((a[i]-mul(c[i],x,p[i]))%p[i]+p[i])%p[i],p[i]);
if(t==-1) return -1;
LL nm=lcm(m,p[i]/gcd(c[i],p[i]));
x=(x%nm+mul(t,m,nm))%nm;
m=nm;
}
return search(x,Base,m);
}
LL w[N];
multiset<LL> sward;
multiset<LL>::iterator it;
void solve()
{
int n,m;
scanf("%d %d",&n,&m);
sward.clear();
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)
scanf("%lld",&p[i]);
for(int i=1;i<=n;i++)
scanf("%lld",&w[i]);
for(int i=1;i<=m;i++)
{
LL s;
scanf("%lld",&s);
sward.insert(s);
}
for(int i=1;i<=n;i++)
{
it=sward.begin();
if(a[i]<(*it)) c[i]=(*it);
else
{
it=sward.upper_bound(a[i]);
it--;
c[i]=(*it);
}
sward.erase(it);
sward.insert(w[i]);
}
LL Base=0;
for(int i=1;i<=n;i++)
{
Base=max(Base,ceil(a[i],c[i]));
}
for(int i=1;i<=n;i++)
c[i]%=p[i],a[i]%=p[i];
printf("%lld\n",EXCRT(n,Base));
}
int main()
{
// freopen("P477_5.in","r",stdin);
int T;
cin>>T;
while(T--)
{
solve();
}
return 0;
}
D2T2
D2T3
只会50pts
20pts n ≤ 20 n\leq 20 n≤20
暴力建出图,然后用状压跑哈密顿回路计数
15pts A,K=1
答案只可能是0,1,2,三者之一
跑dfs即可搜出来
30pts K=1
考虑dp
设
f
[
x
]
[
0
/
1
/
2
]
f[x][0/1/2]
f[x][0/1/2]
分别表示,以x为根的子树,走完的方案数
1表示从x一直走到最靠左的节点,然后再走完整棵树从最右边出去的方案数
2表示从x一直走到最靠右的节点,然后再走完整棵数从最左边出去的方案数
0表示从从一边叶子进去,走完整棵数从另一端的叶子出去的方案数
转移
设son[1……len]分别是x按照dfs序排列的儿子
f
[
x
]
[
1
]
=
f
[
s
o
n
[
x
]
[
1
]
]
∗
∏
i
=
2
l
e
n
f
[
s
o
n
[
x
]
]
[
0
]
f[x][1]=f[son[x][1]]*\prod_{i=2}^{len}f[son[x]][0]
f[x][1]=f[son[x][1]]∗i=2∏lenf[son[x]][0]
2,0都是类似的,看代码应该能看懂
总共50pts
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
typedef long long LL;
const int mod = 998244353;
vector<int> son[N];
struct edge
{
int y,next;
}e[2*N];
int link[N],t=0;
void add(int x,int y)
{
e[++t].y=y;
e[t].next=link[x];
link[x]=t;
}
LL f[N][4],ans=0;
void dfs(int x,int pre)
{
for(int i=link[x];i;i=e[i].next)
{
int y=e[i].y;
if(y==pre) continue;
son[x].push_back(y);
}
sort(son[x].begin(),son[x].end());
for(auto y:son[x])
dfs(y,x);
int len=(int)son[x].size()-1;
if(len==-1)
{
f[x][0]=1;
f[x][1]=1;
f[x][2]=1;
return;
}
LL res=1;
for(int i=1;i<=len;i++)
res=res*f[son[x][i]][0]%mod;
f[x][1]=f[son[x][0]][1]*res%mod;
res=1;
for(int i=0;i<len;i++)
res=res*f[son[x][i]][0]%mod;
f[x][2]=f[son[x][len]][2]*res%mod;
if(len>=1)
{
for(int i=0;i<len;i++)
{
res=1;
for(int j=0;j<i;j++) res=res*f[son[x][j]][0]%mod;
for(int j=i+2;j<=len;j++) res=res*f[son[x][j]][0]%mod;
res=res*f[son[x][i]][2]%mod;
res=res*f[son[x][i+1]][1]%mod;
f[x][0]=(f[x][0]+res)%mod;
}
}
if(x==1&&len>=1)
{
ans=1;
ans=f[son[x][0]][1]*f[son[x][len]][2]%mod;
for(int i=1;i<len;i++)
ans=ans*f[son[x][i]][0]%mod;
}
}
int n,k;
int seq[N];
int tot=0;
void get(int x,int pre)
{
for(int i=link[x];i;i=e[i].next)
{
int y=e[i].y;
if(y==pre) continue;
son[x].push_back(y);
}
sort(son[x].begin(),son[x].end());
if((int)son[x].size()==0)
{
seq[++tot]=x;
return;
}
for(auto y:son[x])
get(y,x);
}
LL F[1<<21][21];
void put(int S)
{
for(int i=1;i<=n;i++)
if((S>>(i-1))&1)
{
cout<<i<<"--->";
}
cout<<"=="<<endl;
}
LL Pow(LL a,LL b)
{
LL res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int main()
{
cin>>n>>k;
for(int i=1;i<n;i++)
{
int u;
scanf("%d",&u);
add(u,i+1);
add(i+1,u);
}
if(k==1)
{
dfs(1,0);
printf("%lld\n",(ans+f[1][0])%mod);
return 0;
}
get(1,0);
for(int i=1;i<=tot;i++)
{
for(int j=i+1;j<=tot;j++)
{
if(min(j-i,tot-(j-i))<=k)
{
add(seq[i],seq[j]);
add(seq[j],seq[i]);
}
}
}
F[1][1]=1;
int s=(1<<n);
for(int S=1;S<s;S++)
{
for(int x=1;x<=n;x++)
{
if(!((S>>(x-1))&1)) continue;
if(!F[S][x]) continue;
for(int i=link[x];i;i=e[i].next)
{
int y=e[i].y;
if(((S>>(y-1))&1)) continue;
F[S+(1<<(y-1))][y]=(F[S+(1<<(y-1))][y]+F[S][x])%mod;
}
}
}
LL res=0;
for(int x=2;x<=n;x++)
{
bool f=0;
for(int i=link[x];i;i=e[i].next)
{
int y=e[i].y;
if(y==1)
{
f=1;
break;
}
}
if(f==1) res=(res+F[s-1][x])%mod;
}
cout<<res*Pow(2,mod-2)%mod;
return 0;
}