1. 程式人生 > 實用技巧 >GMOJ 1283排列統計 題解

GMOJ 1283排列統計 題解

Tips:矩陣ruo

思路

上栗子

A: 2 3 1

首先,對於這樣的一個 \(A\) 的排列,先把它轉換成矩陣,其中第 \(i\) 行第 \(j\) 列表示第 \(i\) 個位置是 \(j\)

  數字 |   |   |   |
位置   | 1 | 2 | 3 |
------+---+---+---+
    1 | 0 | 1 | 0 |
------+---+---+---+
    2 | 0 | 0 | 1 |
------+---+---+---+
    3 | 1 | 0 | 0 |
------+---+---+---+

奇醜無比

首先,明顯的,每一行每一列都只有一個數。 廢話

然後我們注意到對於一個 \(B_i\) ,表示的是這個矩陣中從 \((1,1)\)\((i,i)\)\(1\) 的個數。

現在,假設我們要構造一個滿足從 \(1\le j\le i\) 的所有 \(B_j\) 限制的一個 \(i\times i\) 的矩陣,且我們已經構造好了前 \((i-1)\times (i-1)\)

比如說長這樣

0 1 ?
0 0 ?
???

現在 \(i\)\(3\) ,已經構造好了前\((i-1)\times (i-1)\) ,那麼我們肯定是要在"?"處放置一些1,使得其滿足 \(B_i\) 的限制。

首先對於綠色部分,由於它會對 \(B_i\)

產生貢獻,且剛好滿足 \(B_{i-1}\) 的限制,那麼在"?"處放置的1的總數肯定為 \(B_i-B_{i-1}\)

由於可用部分一定只有一行一列,所以必然放不下 \(3\) 個或更多的1,於是我們只有 \(3\) 種情況:

\[\begin{cases}0&\text{那麼無操作}\\1&\text{那麼在任意合法位置放一個}\\2&\text{那麼在豎排與橫排各放一個,交點不合法}\end{cases} \]

(建議把三種情況都理解了再繼續)


可以發現對於已經構造好的矩陣,其對於方案數的影響只在於其含有多少個1,位置無關緊要。而根據定義,其含有1的多少恰好就是 \(B_{i-1}\)

。那麼,從第 \(i-1\) 到第 \(i\) 個矩陣的轉移就為:

\[\begin{cases}\times 1&B_i-B_{i-1}=0\\\times (2i-1-2B_{i-1})&B_i-B_{i-1}=1\\\times(i-1-B_{i-1})^2&B_i-B_{i-1}=2\end{cases} \]

\(2i-1\)為總位置數,\(B_{i-1}\) 為一行/列被佔用的數量)

那麼,對於每一個矩陣,都有一樣的轉移,直接乘起來即可。

實現

記得打高精度。

程式碼

#include<cmath>
#include<cstdio>
#define mo 100000000
using namespace std;
int n,b[100010];
struct big{
	long long num[2000];
	big operator *= (int x){
		int i;
		for(i=1;i<=num[0];i++){
			num[i]*=x;
		}
		for(i=1;i<=num[0]||num[i]>0;i++){
			num[i+1]+=num[i]/mo;
			num[i]%=mo;
		}
		num[0]=i-1;
		return(*this);
	}
	big(){
		num[0]=num[1]=1;
	}
}ans; 
void read(int &x){
	char c=getchar();
	for(;c<33;c=getchar());
	for(x=0;(c>47)&&(c<58);x=x*10+c-48,c=getchar());
}
void write(big a){
	for(int i=a.num[0];i;i--){
		if(i<a.num[0]){
			for(int j=mo/10;j>1;j/=10){
				if(a.num[i]<j){
					printf("0");
				}
			}
		}
		printf("%lld",a.num[i]);
	}
}
int main(){
	read(n);
	for(int i=1;i<=n;i++){
		read(b[i]);
		if(b[i]-b[i-1]==1){
			ans*=(2*i-1-2*b[i-1]);
		}else if(b[i]-b[i-1]==2){
			ans*=(i-1-b[i-1])*(i-1-b[i-1]);
		}
	}
	write(ans);
}