【题目链接】
【题意】
询问有多少个长度为
n
n
n 的正整数序列
{
a
i
}
\{a_i\}
{ai}满足以下条件:
LIS
(
{
a
i
}
)
=
max
{
a
i
}
\text{LIS}(\{a_i\})=\max\{a_i\}
LIS({ai})=max{ai}
也就是最长上升子序列的长度等于序列的最大值。换句话说,如果序列最大值是
k
k
k ,那么满足条件的序列等价于其中存在子序列
1
,
2
,
.
.
k
1,2,..k
1,2,..k 。
n ≤ 3000 n\le 3000 n≤3000
此外,还要询问这些序列中,每个 [ 1 , n ] [1,n] [1,n] 的整数分别出现的总次数。
为了防止打表通过此题,每组数据输入一个模数 p ( ≤ 1 0 9 ) p(\le 10^9) p(≤109) ,输出所有答案模意义下的结果。
【思路】
给每个符合要求的序列确定一个唯一标识,将有利于计数。
令 f ( n , k ) f(n,k) f(n,k) 表示长度为 n n n 的序列,且序列中的最大值为 k k k 的序列方案数。
为了方便表示 f ( n , k ) f(n,k) f(n,k) 我们对每个序列用其最靠前(即下标字典序最小的)的 1 , 2 , . . . k 1,2,...k 1,2,...k 子序列作为标识。
假设这 k k k 个标识位已经确定,我们发现对于标识位的这 k k k 个数字,
1 1 1 前面的数字只能塞 2 , 3 , . . . k 2,3,...k 2,3,...k 这些数字,
1 , 2 1,2 1,2 之间只能塞 1 , 3 , . . . k 1,3,...k 1,3,...k 这些数字
……
k − 1 , k k-1,k k−1,k 之间只能塞 1 , 2 , . . . k − 1 1,2,...k-1 1,2,...k−1 这些数字。
而 k k k 后面可以塞不超过 k k k 的任意数字。
所以假设 k k k 后面有 d d d 个数字,那么算上前面的 n − k − d n-k-d n−k−d 个空位,一共有 k d ( k − 1 ) n − k − d k^d(k-1)^{n-k-d} kd(k−1)n−k−d 种情况。
所以我们考虑枚举 d d d ,并且用组合数 ( n − d − 1 k − 1 ) \binom{n-d-1}{k-1} (k−1n−d−1) 来表示对 k k k 个标识位的确定。
于是乎得到
f
(
n
,
k
)
=
∑
d
=
0
n
−
k
(
n
−
d
−
1
k
−
1
)
k
d
(
k
−
1
)
n
−
k
−
d
f(n,k) = \sum_{d=0}^{n-k}\binom{n-d-1}{k-1} k^d(k-1)^{n-k-d}
f(n,k)=d=0∑n−k(k−1n−d−1)kd(k−1)n−k−d
那么序列计数答案就是:
∑
k
=
1
n
f
(
n
,
k
)
\sum_{k=1}^n f(n,k)
k=1∑nf(n,k)
预处理组合数和幂次,可以
O
(
n
2
)
O(n^2)
O(n2) 解决。
接下来考虑每个数字出现了多少次,我们考虑 [ 1 , k ] [1,k] [1,k] 在 f ( n , k ) f(n,k) f(n,k) 序列中出现的次数。
根据上面推导过程中,每个位置可以塞数字的情况,容易发现
k
k
k 个数字的出现次数是均匀的(其实感知一下也能猜出这个性质),所以每个数字在最大值为
k
k
k 的合法序列中的出现次数是:
n
k
∑
d
=
0
n
−
k
(
n
−
d
−
1
k
−
1
)
k
d
(
k
−
1
)
n
−
k
−
d
\frac{n}{k}\sum_{d=0}^{n-k}\binom{n-d-1}{k-1} k^d(k-1)^{n-k-d}
knd=0∑n−k(k−1n−d−1)kd(k−1)n−k−d
可以发现对于
d
>
0
d>0
d>0 的时候,外面的
1
k
1\over k
k1 可以直接乘进去,只有
d
=
0
d=0
d=0 的时候,需要特殊处理一下这一项,即:
n
k
(
n
−
1
k
−
1
)
(
k
−
1
)
n
−
k
=
n
⋅
(
n
−
1
)
!
k
⋅
(
k
−
1
)
!
(
n
−
k
)
!
(
k
−
1
)
n
−
k
=
(
n
k
)
(
k
−
1
)
n
−
k
\frac{n}{k} \binom{n-1}{k-1}(k-1)^{n-k}=\frac{n\cdot (n-1)!}{k\cdot(k-1)!(n-k)!}(k-1)^{n-k}=\binom{n}{k}(k-1)^{n-k}
kn(k−1n−1)(k−1)n−k=k⋅(k−1)!(n−k)!n⋅(n−1)!(k−1)n−k=(kn)(k−1)n−k
因此我们可以定义
g
(
x
,
k
)
g(x,k)
g(x,k) 表示数字
x
x
x 在序列最大值是
k
k
k 的合法序列中的出现次数,并且根据刚才的推导,有:
g
(
x
,
k
)
=
{
0
x
>
k
(
n
k
)
(
k
−
1
)
n
−
k
+
n
∑
d
=
1
n
−
k
(
n
−
d
−
1
k
−
1
)
k
d
−
1
(
k
−
1
)
n
−
k
−
d
x
≤
k
g(x,k)=\begin{cases} 0 & x>k \\ \binom{n}{k}(k-1)^{n-k}+ n\sum_{d=1}^{n-k}\binom{n-d-1}{k-1} k^{d-1}(k-1)^{n-k-d} & x\le k \end{cases}
g(x,k)={0(kn)(k−1)n−k+n∑d=1n−k(k−1n−d−1)kd−1(k−1)n−k−dx>kx≤k
求出之后,数字
x
x
x 的总出现次数就是
∑
i
=
x
n
g
(
x
,
k
)
\sum_{i=x}^{n}g(x,k)
∑i=xng(x,k) 其实就是后缀和啦。
同理这个式子也就可以 O ( n 2 ) O(n^2) O(n2) 求解了。
【代码】
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 3010
using namespace std;
int n,p;
int c[MAXN][MAXN];
int power[MAXN][MAXN];
void get_c()
{
c[0][0]=1;
for(int i=1;i<=n;i++)
{
c[i][0]=1;
for(int j=1;j<=i;j++)
c[i][j]=(c[i-1][j-1]+c[i-1][j])%p;
}
for(int i=0;i<=n;i++)
{
power[i][0]=1;
for(int j=1;j<=n;j++)
power[i][j]=1ll*power[i][j-1]*i%p;
}
}
int s[MAXN];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&p);
get_c();
int ans=0;
s[n+1]=0;
for(int k=n;k>=1;k--)
{
for(int d=0;d<=n-k;d++)
{
long long tmp=1ll*c[n-d-1][k-1]*power[k-1][n-k-d]%p;
ans=(ans+tmp*power[k][d])%p;
if(d>0)
s[k]=(s[k]+tmp*power[k][d-1]%p*n)%p;
else
s[k]=1ll*c[n][k]*power[k-1][n-k]%p;
}
s[k]=(s[k+1]+s[k])%p;
}
printf("%d\n",ans);
for(int i=1;i<=n;i++)printf("%d%c",s[i],i==n?'\n':' ');
}
return 0;
}