1. 程式人生 > >Codeforces Good Bye 2018 D (1091D) New Year and the Permutation Concatenation

Codeforces Good Bye 2018 D (1091D) New Year and the Permutation Concatenation

題意:給n!個n的排列,按字典序從小到大連成一條序列,例如3的情況為:[1,2,31,3,22,1,,2,3,,3,1,,3,2,1],問其中長度為n,且和為sum=n*(n+1)/2的序列有多少個?

思路(官方題解):我們考慮一下next_perumation函式產生字典序遞增的全排列的過程:

假設某一個序列長度為n,最長的遞減的字尾長度k,那麼它的下一個排列是這樣產生的:選取序列第n-k個數,與後k個數中最小的那個交換(其實就是最後一個),然後將後k個數按從小到大排序。

例如序列1,2,5,4,3的下一個排列為1,3,2,4,5。我們觀察發現:這種時候1,2,(5,4,3,1,3,)2,4,5不滿足和為sum了,因為在產生下一個排列的過程中,第n-k個位置的數被替換了。

也就是說,假設一個序列存在長度為k的遞減字尾,那麼這個字尾不能產生一個長度為sum的序列。例如,1,2,(5,4,3,1,3,)2,4,5不行,但是1,(2,5,4,3,1,)3,2,4,5可以。

所以,我們的任務是找出每個長度為k的遞減字尾有多少個?應該為C(n,n-k)*(n-k)!=A(n,n-k)=n!/k!個。因為只要選了前面n-k個數,後面長度為k的遞減的序列是固定的,所以我們只需要選n-k個數全排列就行了。

我們可以得到最終的答案了:一共有n*n!-(n-1)個序列,要減去( ∑(k from 1 to n-1) n!/k!  )- (n-1)個。

為什麼要減去n-1個呢?我們來看最後一個排列(假設n為5)5,4,3,2,1 。5之後的序列不存在,所以要從總的序列數中減去。而這(n-1)個不存在的序列恰好會被判定為不滿足題意,也應該減去。

所以總的來說,答案應該是:(所有的序列-不存在的序列)-(不滿足的序列-不存在的序列)。我們可以把答案寫的更優雅一點:ans=n*n!-∑(k from 1 to n-1) n!/k!。

程式碼:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<map>
#include<set>
#include<bitset>
#include<queue>
#include<vector>
#include<stack>
#define INF 0x3f3f3f3f
#define pii pair<int,int>
#define LL long long
#define fi first
#define se second
#define ls(x) (x<<1)
#define rs(x) ((x<<1)+1)
#define lowbit(x) (x&(-x))
using namespace std;
const int maxn=1000010;
const LL mod=998244353;
LL s[maxn],f[maxn];//s[k]是n!/k!
int main(){
	LL n;
	scanf("%lld",&n);
	f[0]=1,s[n]=1;
	for(LL i=1;i<=n;i++){
		f[i]=(f[i-1]*i)%mod;
	}
	for(LL i=n-1;i>=1;i--){
		s[i]=(s[i+1]*(i+1))%mod;
	}
	LL ans=(n*(f[n]))%mod;
	for(LL i=1;i<=n-1;i++){
		ans=(ans-s[i]+mod)%mod;
	}
	cout<<ans<<endl;
}