对于一个序列 ,定义 ,满足 。特别地,若 ,表示不存在这样的 。
给定一个序列 和值域大小 ,满足 ,请求出有多少个序列 满足:
- 对于所有满足 的 ,都有
答案对 取模,满足答案至少为 。
。
观察到若 ,那么一定有 。
所以由于答案至少为 ,那么若对于所有满足 的 都从 向 连一条有向边,这些边肯定不会相交:

观察到这些边构成了包含关系,并且内层的值一定小于等于外层的,所以不难想到堆,那么考虑建立一棵树,跑树形 dp。不难发现,关系只有大于和大于等于两种,所以考虑给树加上边权,边权为 表示父亲的值要大于儿子,边权为 表示父亲的值要大于等于儿子。
首先虚拟出一个点 ,令 。这么做是为了让所有 联通,最后能够组成一棵树。
考虑 的点。显然,设 表示包含 的最内层的边的右端点,在树上只要让 的父亲为 ,边权为 即可。特别的,若 ,那么要让 的父亲为 ,边权为 :

接下来考虑 的点,朴素的想法是在树上让 的父亲为 ,边权为 。但是考虑如下情况:

显然要满足 ,那么在树上这样建边即可:

代码实现也不难,倒着扫即可。
建出树之后直接跑 dp 即可,在此不过多赘述。
代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
inline int read()
{
int s=0,w=1,c=getchar();
while(c<'0'||c>'9') c=='-'?w=-1,c=getchar():c=getchar();
while(c>='0'&&c<='9') s=s*10+c-'0',c=getchar();
return s*w;
}
const int S=305,MS=100005,SS=1000005,p=1000000007;
int n,m;
int pre[S],lst[S];
int esum,to[SS],c[SS],nxt[SS],h[S];
int dp[S][MS];
inline void add(int x,int y,int w)
{
to[++esum]=y;
c[esum]=w;
nxt[esum]=h[x];
h[x]=esum;
}
inline void addd(int &x,int y)
{
x+=y;
if(x>=p) x-=p;
}
void dfs(int u)
{
for(int j=1;j<=m;j++) dp[u][j]=1;
for(int i=h[u];i;i=nxt[i])
{
int v=to[i],w=c[i];
dfs(v);
int presm=0;
for(int j=1;j<=m;j++)
{
addd(presm,dp[v][j-w]);
dp[u][j]=1ll*dp[u][j]*presm%p;
}
}
}
int main()
{
freopen("stack.in","r",stdin);
freopen("stack.out","w",stdout);
n=read();
m=read();
for(int i=1;i<=n;i++) pre[i]=read();
for(int i=1;i<=n;i++) lst[i]=i;
pre[n+1]=0;
for(int i=n;i>=1;i--)
{
if(pre[i]!=-1)
{
add(lst[pre[i]],i,lst[pre[i]]==pre[i]&&pre[i]!=0);
lst[pre[i]]=i;
}
else
{
for(int j=i+1;j<=n+1;j++)
{
if(pre[j]!=-1&&pre[j]<i)
{
add(j==n+1?0:j,i,0);
break;
}
}
}
}
dfs(0);
printf("%d\n",dp[0][m]);
return 0;
}