Problem Statement
给出整数A、B和M。
要找出满足以下所有条件的排列P=(P1, …, P(AB-1))的数量,求这个计数对M取模的结果。
P的最长递增子序列的长度是A。
P的最长递减子序列的长度是B。
存在一个整数n,使得将n+0.5附加到P的末尾不会改变最长递增子序列和最长递减子序列的长度。
杨表:
杨表是一种特殊的矩阵。
标准杨表:同列数字严格递增,且同行数字单调递增的杨表
用排列构造杨表
RSK 插入算法提供了一个将杨表和排列联系起来的途径。它由 Robinson, Schensted 和 Knuth 提出。
令 S 是一个杨表,定义 S←xS \leftarrow xS←x 表示将 xxx 从第一行插入杨表中,具体如下:
在当前行中找到最小的比 xxx 大的数 yyy。
如果找到了,用 x 去替换 y,移到下一行,令 x ←\leftarrow← y 重复操作 1。
如果找不到,就把 x 放在该行末尾并退出。记 x 在第 s 行第 t 列,(s, t) 必定是一个边角。一个格子 (s, t) 是边角当且仅当 (s + 1, t) 和 (s, t + 1) 都不存在格子。
性质
- 插入时被替换的路径一定是向下且向左的
定义
- 一个非负整数 n(总方格数)的 整数分拆(integer partition)λ\lambdaλ有PnP_nPn(划分数)个。
- 可以用一个整数分拆表示一个杨表 λ=(λ1,λ2…)\lambda = (\lambda{1}, \lambda{2} \ldots)λ=(λ1,λ2…)
- 给定一个共有 n 个方格的杨表 πλ\pi_{\lambda}πλ,把 1 到 n 的 n 个数字填入杨表中,使得每行从左到右,每列从下到上都是递增的。用 dimπλdim_{\pi_{\lambda}}dimπλ 表示可以这样填的方法个数。
- 勾长 hook(v)\mathrm{hook}(v)hook(v) 等于同行右边的方格数加上同列上面的方格数,再加 1(即方格本身)。
勾长公式
dimπλ=n!∏x∈Y(λ)hook(x).\dim \pi _{\lambda}={\frac {n!}{\prod_{{x\in Y(\lambda)}}{\mathrm {hook}}(x)}}.dimπλ=∏x∈Y(λ)hook(x)n!.
LIS
对于杨表 PPP, 定义对于一个从 1 到 n 的排列 X=x1,…,xnX = x_{1}, \ldots , x_{n}X=x1,…,xn。
- PXP_{X}PX 中第一行的长度即为排列 X 的 最长上升子序列(LIS) 长度。注意,P 的第一行并不一定是 LIS 本身.
- 杨表 PXP_{X}PX 中的第一列长度即为排列 X 的 最长下降子序列(LDS) 长度。
- 杨表前 k 列的长度之和就是LIS不超过k的最长的子序列的长度
- Schensted correspondence: 两个形状相同的杨表组成的杨表对(内容可以相同也可以不同),与排列一一对应。一个杨表记录排列本身,另一个记录杨表中每一个位置是在第几轮被首次填上数的,那我们就可以用倒推的方式唯一确定一个序列。2个杨表都是标准杨表。
KaTeX parse error: Undefined control sequence: \P at position 22: …m_{\lambda \in \̲P̲_n} (\dim \pi _…
#include<bits/stdc++.h>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define ForkD(i,k,n) for(int i=n;i>=k;i--)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define Forpiter(x) for(int &p=iter[x];p;p=next[p])
#define Lson (o<<1)
#define Rson ((o<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,0x3f,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define MEMx(a,b) memset(a,b,sizeof(a));
#define INF (0x3f3f3f3f)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define vi vector<int>
#define pi pair<int,int>
#define SI(a) ((a).size())
#define Pr(kcase,ans) printf("Case #%d: %lld\n",kcase,ans);
#define PRi(a,n) For(i,n-1) cout<<a[i]<<' '; cout<<a[n]<<endl;
#define PRi2D(a,n,m) For(i,n) { \
For(j,m-1) cout<<a[i][j]<<' ';\
cout<<a[i][m]<<endl; \
}
#pragma comment(linker, "/STACK:102400000,102400000")
#define ALL(x) (x).begin(),(x).end()
#define gmax(a,b) a=max(a,b);
#define gmin(a,b) a=min(a,b);
#define sqr(x) (x*x)
#define LOCAL
#ifdef LOCAL
# define debug(...) debug_impl(#__VA_ARGS__, __VA_ARGS__)
template <class H, class... Ts> void debug_impl(const char* s, const H& h, const Ts&... t) {
cerr << "[\033[32mDEBUG\033[m] " << s << ": " << h, ((cerr << ", " << t), ..., (cerr << "\n"));
}
#else
# define debug(...) void(0)
#endif
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
ll a,b,F;
ll pow2(ll a,int b,ll p) //a^b mod p
{
if (b==0) return 1%p;
if (b==1) return a%p;
ll c=pow2(a,b/2,p)%p;
c=c*c%p;
if (b&1) c=c*a%p;
return c%p;
}
ll inv(ll a,ll p) { //gcd(a,p)=1
return pow2(a,p-2,p);
}
void bfs(){
vi st(a,0);
map<vi ,ll> dp0;
dp0[st]=1;
for(int i=1;i<=a*b-1;i++) {
map< vi ,ll> dp1;
for(auto p:dp0) {
auto vec=p.fi; auto w=p.se;
Rep(i,a) {
if(((i<a-1 &&vec[i]<b) || (i==a-1&& vec[i]<b-1)) && (i==0|| vec[i-1]>vec[i]) && !(i==a-1 && vec[a-1]+1==vec[a-2] ) ) {
auto vec2=vec;
vec2[i]++;
dp1[vec2]=(dp1[vec2]+w)%F;
}
}
}
swap(dp0,dp1);
}
vi ed(a,b);
ed[a-1]--;
ll ans=1;
For(i,a) For(j,b) {
if(i==a && j==b) continue;
ans=ans*(-1+a-(j==b)-i+1+b-(i==a)-j+1)%F;
}
ll p=1;For(i,a*b-1) p=p*i%F;
cout<<p*inv(ans,F)%F*dp0[ed]%F<<endl;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
cin>>a>>b>>F ;
bfs();
return 0;
}

被折叠的 条评论
为什么被折叠?



