【题目描述】
定义一个排列 a 的价值为满足|a[i]-i|<=1 的 i 的数量。
给出三个正整数 n,m,p,求出长度为 n 且价值恰好为 m 的排列的个数对 p 取
模的结果。
【输入描述】
第一行两个正整数 T,p,T 为数据组数,p 为模数。
接下来 T 行,每行两个正整数 n,m。
【输出描述】
T 行,每行一个非负数,表示答案。
【输入样例】
5 1887415157
3 1
3 2
3 3
50 10
1500 200
【输出样例】
1
2
3
621655247
825984474
【数据范围】
10%的数据:n<=10
30%的数据:n<=15
50%的数据:n<=200
另有 10%的数据:m=1
另有 10%的数据:m=n-1
100%的数据:1<=T,n,m<=2000,2<=p<=10^12
题意:
定义一个排列 a 的价值为满足|a[i]-i|<=1 的 i 的数量。
给出三个正整数 n,m,p,求出长度为 n 且价值恰好为 m 的排列的个数对 p 取模的结果。
T组询问,p事先给出。
题解:
因为n,m <= 2000,而且p是事先给出的,所以我们可以一次性预处理出n,m <= 2000的答案。
考虑一个长度为i的排列如何变成长度为i+1的排列。
一种情况是我在它末尾加入了一个数i+1,另一种情况是我用i+1替换掉了原来排列中的一个数,然后把被换掉的数放到排列的末尾。
那么,这个排列权值的变化就是:
第一种情况:在它末尾加入了一个数i+1,权值+1。
第二种情况:用i+1替换掉一个数,权值 += 加的贡献 - 换掉的数的贡献。
在DP当中,我们只需要考虑替换掉的数是否是i,以及i是否在位置i/i-1即可。总共有5种本质不同的状态,分类讨论转移即可。
复杂度O(nm)。
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=2005; int q,n,m,u,v; LL p,ans,f[N][N][5],x; int rd(){ int re=0,f=1;char c=getchar(); while ((c<'0')||(c>'9')) {if (c=='-') f=-f;c=getchar();} while ((c>='0')&&(c<='9')) {re=re*10+c-'0';c=getchar();} return re*f; } int main(){ freopen("c.in","r",stdin); freopen("c.out","w",stdout); cin>>q>>p; memset(f,0,sizeof(f)); f[1][1][0]=1ll; f[2][2][0]=1ll;f[2][2][1]=1ll; n=2000; for (int i=2;i<n;++i) for (int j=0;j<=n;++j){ for (int k=0;k<=4;++k){ x=f[i+1][j+1][0]+f[i][j][k]; x=(x<p)?x:(x-p); f[i+1][j+1][0]=x; u=j+((k%2)==0); v=1+(k!=0); x=f[i+1][u][v]+f[i][j][k]; x=(x<p)?x:(x-p); f[i+1][u][v]=x; } if (f[i][j][0]>0ll){ f[i+1][j-1][4]=(f[i+1][j-1][4]+f[i][j][0]*(LL)(j-1))%p; f[i+1][j][4]=(f[i+1][j][4]+f[i][j][0]*(LL)(i-j))%p; } if (f[i][j][1]>0ll){ x=f[i+1][j][3]+f[i][j][1]; x=(x<p)?x:(x-p); f[i+1][j][3]=x; f[i+1][j-1][4]=(f[i+1][j-1][4]+f[i][j][1]*(LL)(j-2))%p; f[i+1][j][4]=(f[i+1][j][4]+f[i][j][1]*(LL)(i-j))%p; } if (f[i][j][2]>0ll){ x=f[i+1][j][3]+f[i][j][2]; x=(x<p)?x:(x-p); f[i+1][j][3]=x; f[i+1][j-1][4]=(f[i+1][j-1][4]+f[i][j][2]*(LL)(j-1))%p; f[i+1][j][4]=(f[i+1][j][4]+f[i][j][2]*(LL)(i-j-1))%p; } if (f[i][j][3]>0ll){ x=f[i+1][j+1][3]+f[i][j][3]; x=(x<p)?x:(x-p); f[i+1][j+1][3]=x; f[i+1][j-1][4]=(f[i+1][j-1][4]+f[i][j][3]*(LL)(j-1))%p; f[i+1][j][4]=(f[i+1][j][4]+f[i][j][3]*(LL)(i-j-1))%p; } if (f[i][j][4]>0ll){ x=f[i+1][j+1][3]+f[i][j][4]; x=(x<p)?x:(x-p); f[i+1][j+1][3]=x; if (j>0) f[i+1][j-1][4]=(f[i+1][j-1][4]+f[i][j][4]*(LL)(j))%p; f[i+1][j][4]=(f[i+1][j][4]+f[i][j][4]*(LL)(i-j-2))%p; } } for (;q>0;--q){ cin>>n>>m; ans=(f[n][m][0]+f[n][m][1]+f[n][m][2]+f[n][m][3]+f[n][m][4])%p; cout<<ans<<'\n'; } return 0; }