题意
对于序列 a a a,记 v i v_i vi 为位置距离 a i a_i ai 最近的、比 a i a_i ai 大的数与它的距离(假设 a 0 a_0 a0 和 a n + 1 a_{n+1} an+1 都为无穷大)。
T T T 次询问,给定 n , x n,x n,x,求有多少长度为 n n n 的排列 p p p 使得 ∑ i = 1 n v i = x \sum\limits_{i=1}^nv_i=x i=1∑nvi=x。答案对给定的质数 P P P 取模。 n ≤ 200 n\leq 200 n≤200, T ≤ 10 T\leq 10 T≤10。
题解
记 F i F_i Fi 为长为 i i i 的排列的生成函数,系数为 ∑ v i \sum v_i ∑vi。通过枚举最大值的位置容易通过 F 0 ∼ F i − 1 F_0\sim F_{i-1} F0∼Fi−1 得到 F i F_i Fi。
冷静打表分析一波可知
∑
v
i
\sum v_i
∑vi 最大是
O
(
n
log
n
)
O(n\log n)
O(nlogn) 的,记
max
∑
v
i
\max{\sum v_i}
max∑vi 为
V
V
V。
用 F i F_i Fi 的从 1 到 V + 1 V+1 V+1 的点值来计算,转移的总复杂度为 n l o g n n^log n nlogn。
每次询问随便拉格朗日插值一下,单次询问复杂度为 n 2 log n n^2\log n n2logn
代码:
#include<bits/stdc++.h>
using namespace std;
int getint(){
int ans=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans*f;
}
const int V=750,N=210;
int mod=0;
int queryn[20],queryx[20];
int maxv[N];
int p[V][V];
int f[N][V];
int inv[V];
int c[N][N];
int pre[V][V],suf[V][V];//\prod (x-i)
int qpow(int x,int y){
int ans=1;
while(y){
if(y&1)ans=ans*1ll*x%mod;
x=x*1ll*x%mod;
y>>=1;
}
return ans;
}
void init(int n,int v){
for(int i=0;i<=v;i++){
p[i][0]=1;
for(int j=1;j<=v;j++){
p[i][j]=p[i][j-1]*1ll*i%mod;
}
}
c[1][1]=1;
for(int i=2;i<=n+1;i++){
for(int j=1;j<=i;j++){
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
}
for(int i=0;i<=n;i++)
for(int j=0;j<=i;j++)
c[i][j]=c[i+1][j+1];
for(int i=1;i<=v;i++)inv[i]=qpow(i,mod-2);
for(int i=1;i<=v;i++)f[0][i]=1,f[1][i]=i;
for(int i=2;i<=n;i++){
for(int j=1;j<=i/2;j++){
//x^j f(j-1)f(i-j) C(i-1,j-1)
for(int k=1;k<=v;k++){
f[i][k]=(f[i][k]+
p[k][j]*2ll*f[j-1][k]%mod*f[i-j][k]%mod%mod*c[i-1][j-1])%mod;
}
}
if(i&1){
int j=(i+1)>>1;
for(int k=1;k<=v;k++){
f[i][k]=(f[i][k]+
p[k][j]*1ll*f[j-1][k]%mod*f[i-j][k]%mod*c[i-1][j-1])%mod;
}
}
//for(int j=1;j<=v;j++)cerr<<" "<<f[i][j];cerr<<endl;
}
pre[0][0]=1;
for(int i=1;i<=v;i++){
pre[i][0]=1;
for(int j=1;j<=i;j++){
pre[i][j]=(pre[i-1][j]+pre[i-1][j-1]*1ll*(mod-i))%mod;
}
}
suf[v+1][0]=1;
for(int i=v;i>=1;--i){
suf[i][0]=1;
for(int j=1;j<=v-i+1;j++){
suf[i][j]=(suf[i+1][j]+suf[i+1][j-1]*1ll*(mod-i))%mod;
}
}
}
int calc(int m,int p){
int ans=0;
for(int i=0;i<=m;i++){
ans=(ans+pre[p-1][i]*1ll*suf[p+1][m-i])%mod;
}
return ans;
}
int solve(int n,int x,int lim){
if(x>maxv[n])return 0;
int res=0,p=0;
for(int i=1;i<=lim;i++){
p=f[n][i];//y
for(int j=1;j<=lim;j++){
if(i==j)continue;
p=p*1ll*(i>j?inv[i-j]:mod-inv[j-i])%mod;
}
res=(res+p*1ll*calc(lim-x-1,i))%mod;
}
return res;
}
signed main(){
mod=getint();
int q=0;while(cin>>queryn[q]>>queryx[q])q++;
int mxn=0,mxx=0;
for(int i=0;i<q;i++){
mxn=max(mxn,queryn[i]);
mxx=max(mxx,queryx[i]);
}
maxv[1]=1;
for(int i=2;i<=mxn;i++)
for(int j=1;j<=i;j++)
maxv[i]=max(maxv[i],maxv[j-1]+maxv[i-j]+min(j,i-j+1));
mxx=maxv[mxn]+1;
init(mxn,mxx);
for(int i=0;i<q;i++){
int n=queryn[i],x=queryx[i];
printf("%d\n",solve(n,x,mxx));
}
return 0;
}