ABC 378G(Everlasting LIDS-Robinson–Schensted correspondence)

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 xSx 表示将 xxx 从第一行插入杨表中,具体如下:

在当前行中找到最小的比 xxx 大的数 yyy
如果找到了,用 x 去替换 y,移到下一行,令 x ←\leftarrow y 重复操作 1。
如果找不到,就把 x 放在该行末尾并退出。记 x 在第 s 行第 t 列,(s, t) 必定是一个边角。一个格子 (s, t) 是边角当且仅当 (s + 1, t) 和 (s, t + 1) 都不存在格子。
性质

  1. 插入时被替换的路径一定是向下且向左的

定义

  1. 一个非负整数 n(总方格数)的 整数分拆(integer partition)λ\lambdaλPnP_nPn(划分数)个。
  2. 可以用一个整数分拆表示一个杨表 λ=(λ1,λ2…)\lambda = (\lambda{1}, \lambda{2} \ldots)λ=(λ1,λ2)
  3. 给定一个共有 n 个方格的杨表 πλ\pi_{\lambda}πλ,把 1 到 n 的 n 个数字填入杨表中,使得每行从左到右,每列从下到上都是递增的。用 dimπλdim_{\pi_{\lambda}}dimπλ 表示可以这样填的方法个数。
  4. 勾长 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πλ=xY(λ)hook(x)n!.

LIS

对于杨表 PPP, 定义对于一个从 1 到 n 的排列 X=x1,…,xnX = x_{1}, \ldots , x_{n}X=x1,,xn

  1. PXP_{X}PX 中第一行的长度即为排列 X 的 最长上升子序列(LIS) 长度。注意,P 的第一行并不一定是 LIS 本身.
  2. 杨表 PXP_{X}PX 中的第一列长度即为排列 X 的 最长下降子序列(LDS) 长度。
  3. 杨表前 k 列的长度之和就是LIS不超过k的最长的子序列的长度
  4. 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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值