排列最小值

也許更好的閱讀體驗

Description\mathcal{Description}
problem
多組詢問
1n,Q1051 \leq n,Q\leq 10^5

Solution\mathcal{Solution}

5050分解法

考慮DPDP,感覺上直接算答案不好算,所以考慮算長度爲nn的所有排列改變的次數的排列的個數算出來
之後再把個數乘以次數的平方即可
fi,jf_{i,j}表示長度爲ii的排列的改變次數jj排列個數
考慮把最大的ii插入到長度爲i1i-1的改變次數爲jj的排列中,則有ii個空可以插,其中最前面的空插進去會使改變次數加一,所以有i1i-1個空使改變次數不變
考慮插在最前面則原改變次數應爲j1j-1
所以fi,j=fi1,j(i1)+fi1,j1f_{i,j}=f_{i- 1,j}*(i-1)+f_{i-1,j-1}
其實就是第一類斯特林數的遞推公式
提前預處理一下即可做到 n2n^{2}

100100分解法

仍然是DPDP,原來的是把問題拆開,好做但是不能直接算
不妨大膽一點,設fif_i表示長度爲ii答案
仍然考慮從i1i-1轉移過來
還是上面那句
考慮把最大的ii插入到長度爲i1i-1的改變次數爲jj的排列中,則有ii個空可以插,其中最前面的空插進去會使改變次數加一,所以有i1i-1個空使改變次數不變
fi=fi1(i1)+f_i=f_{i-1}*(i-1)+插到最前面的貢獻
考慮插在最前面,原本的改變次數爲xx的都變爲x+1x+1
也就是x2>(x+1)2=x2+2x+1x^2 -> (x+1)^2=x^2+2x+1
我們設gig_i表示原本算貢獻時是按照xx來算的長度爲ii的答案
對於那個11,因爲插在最前面後面的i1i-1個數共有(i1)!(i-1)!種組合
所以fi=fi1(i1)+fi1+2gi1+(i1)!=fi1i+fi1+(i1)!f_i=f_{i-1}*(i-1)+f_{i-1}+2g_{i-1}+(i-1)!=f_{i-1}*i+f_{i-1}+(i-1)!
gig_i同樣這麼推
gi=gi1i+(i1)!g_i=g_{i-1}*i+(i-1)!

Code\mathcal{Code}

/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年09月28日 星期六 09時34分38秒
*******************************/
#include <cstdio>
#include <fstream>
using namespace std;
const int maxn = 100005;
const int mod = 998244353;
//{{{cin
struct IO{
	template<typename T>
	IO & operator>>(T&res){
		res=0;
		bool flag=false;
		char ch;
		while((ch=getchar())>'9'||ch<'0')	flag|=ch=='-';
		while(ch>='0'&&ch<='9')	res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
		if (flag)	res=~res+1;
		return *this;
	}
}cin;
//}}}
int T,n;
int f[maxn],g[maxn],fac[maxn];
int main()
{
	fac[0]=1;
	for (int i=1;i<=100000;++i){
		fac[i]=1ll*fac[i-1]*i%mod;
		f[i]=((1ll*f[i-1]*i%mod+2ll*g[i-1]%mod)%mod+fac[i-1])%mod;
		g[i]=(1ll*g[i-1]*i%mod+fac[i-1])%mod;
	}
	cin>>T;
	while (T--){
		cin>>n;
		printf("%d\n",f[n]);
	}
	return 0;
}

如有哪裏講得不是很明白或是有錯誤,歡迎指正
如您喜歡的話不妨點個贊收藏一下吧

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章