1. 程式人生 > >[cf 1091D]D. New Year and the Permutation Concatenation

[cf 1091D]D. New Year and the Permutation Concatenation

題意:給n!個n的排列,按字典序從小到大連成一條序列,例如3的情況為:[1,2,3, 1,3,2, 2,1,3 ,2,3,1 ,3,1,2 ,3,2,1],問其中長度為n,且和為sum=n*(n+1)/2的序列有多少個?
(題意來自於:https://www.cnblogs.com/pkgunboat/archive/2018/12/31/10201676.html @維和戰艇機,感謝你這麼好的總結方式,借用一下希望不要介意)

思路(非官方題解):官方題解下面有種方法是:There is also a simple recurrence counting the same answer, found by arsijo:d(n)=(d(n−1)+(n−1)!−1)⋅n,我比賽時是用這個方法。

如官方題解思路,有兩種n長度的子序列符合和為n*(n-1)/2:
第一種為n!個組成序列的排列;
第二種為一部分在一個排列中,另一部分在下一個排列中的排列。
顯而易見,第一種有n!種情況,那下面來推第二種:

對於一個n長度的排列,假設我們先固定前k個元素,那麼對於包含這些前k個元素固定的片段,是一系列①前k個元素固定;②後n-k個元素按照字典序從小到大的排列連線而成的,假設這一系列排列的數量為m。

發現這個有什麼用呢?現在取出上面那段片段,我們來尋找裡面排列為③使用一個排列後n-k個元素④使用下一個排列前k個元素的排列的數量,在上面片段中,能找到的排列都滿足,而又由於這些排列夾在上面排列兩兩之間,可得數量為m-1。(就像對於一個隊伍的小朋友,假設兩兩之間插入一個小朋友,那麼插入小朋友的數量為原隊伍小朋友的數量-1)。
那麼有沒有漏網之魚呢?答案是沒有。這個草稿紙上畫下,你就明白了。m中滿足條件的有m-1種,說明只有一種是不滿足的。對於假設考慮只滿足③的,那麼片段的第一個排列是不滿足的,考慮只滿足④的話,片段的最後一個排列是不滿足的。就不展開了。(不會暴露樓主不想打,,,咔咔)
無圖片描述,emm以防卡在奇怪的地方,給上面解釋一個應該不會有用的提示吧


現在還有一個問題,就是m未知,現在來求m:
由①,則有A(n,n-k)種;
而對於①的每種情況
由②,有k!種,又由解釋需要-1,則有(k!-1)種情況。
至此可以得到一個重要的資訊,列為**(③使用一個排列後n-k個元素④使用下一個排列前k個元素)**的排列的數量 = A(n,n-k)*(k!-1)。

知道上述公式後,來求答案:
k∈[1,n];
當k=n時,其實就是組成序列的排列,所以用組成序列排列的公式:n!;
當k!=n時,用上面得出的公式A(n,n-k)*(k!-1);
因此答案是n!+∑(k from 1 to n-1) [A(n,n-k)×(k!-1)]。

#include<stdio.h>
typedef long long ll;
const int maxn = 1e6 + 5;
const ll Mod = 998244353;
	ll a,jie[maxn],djie[maxn],n,res; //jie,djie分別是階乘和A(n,n-k),res是結果
	
int main()
{
	scanf("%I64d",&n);
	jie[0] = 1; djie[0] = 1;
	for(a = 1;a <= n;a ++)
	{
		jie[a] = jie[a-1] * a % Mod;
		djie[a] = djie[a-1] * (n+1-a) % Mod;		
	}
	res = jie[n];
	for(a = 2;a <= n-1;a ++)
		res = (res + (jie[a] - 1) * djie[n-a] % Mod) % Mod;
	printf("%I64d\n",res);
	return 0;
}