排列最小值

也许更好的阅读体验

D e s c r i p t i o n \mathcal{Description} Description
problem
多组询问
1 ≤ n , Q ≤ 1 0 5 1 \leq n,Q\leq 10^5 1n,Q105

S o l u t i o n \mathcal{Solution} Solution

50 50 50分解法

考虑 D P DP DP,感觉上直接算答案不好算,所以考虑算长度为 n n n的所有排列改变的次数的排列的个数算出来
之后再把个数乘以次数的平方即可
f i , j f_{i,j} fi,j表示长度为 i i i的排列的改变次数 j j j排列个数
考虑把最大的 i i i插入到长度为 i − 1 i-1 i1的改变次数为 j j j的排列中,则有 i i i个空可以插,其中最前面的空插进去会使改变次数加一,所以有 i − 1 i-1 i1个空使改变次数不变
考虑插在最前面则原改变次数应为 j − 1 j-1 j1
所以 f i , j = f i − 1 , j ∗ ( i − 1 ) + f i − 1 , j − 1 f_{i,j}=f_{i- 1,j}*(i-1)+f_{i-1,j-1} fi,j=fi1,j(i1)+fi1,j1
其实就是第一类斯特林数的递推公式
提前预处理一下即可做到 n 2 n^{2} n2

100 100 100分解法

仍然是 D P DP DP,原来的是把问题拆开,好做但是不能直接算
不妨大胆一点,设 f i f_i fi表示长度为 i i i答案
仍然考虑从 i − 1 i-1 i1转移过来
还是上面那句
考虑把最大的 i i i插入到长度为 i − 1 i-1 i1的改变次数为 j j j的排列中,则有 i i i个空可以插,其中最前面的空插进去会使改变次数加一,所以有 i − 1 i-1 i1个空使改变次数不变
f i = f i − 1 ∗ ( i − 1 ) + 插到最前面的贡献 f_i=f_{i-1}*(i-1)+插到最前面的贡献 fi=fi1(i1)+插到最前面的贡献
考虑插在最前面,原本的改变次数为 x x x的都变为 x + 1 x+1 x+1
也就是 x 2 − > ( x + 1 ) 2 = x 2 + 2 x + 1 x^2 -> (x+1)^2=x^2+2x+1 x2>(x+1)2=x2+2x+1
我们设 g i g_i gi表示原本算贡献时是按照 x x x来算的长度为 i i i的答案
对于那个 1 1 1,因为插在最前面后面的 i − 1 i-1 i1个数共有 ( i − 1 ) ! (i-1)! (i1)!种组合
所以 f i = f i − 1 ∗ ( i − 1 ) + f i − 1 + 2 g i − 1 + ( i − 1 ) ! = f i − 1 ∗ i + ( i − 1 ) ! f_i=f_{i-1}*(i-1)+f_{i-1}+2g_{i-1}+(i-1)!=f_{i-1}*i+(i-1)! fi=fi1(i1)+fi1+2gi1+(i1)!=fi1i+(i1)!
g i g_i gi同样这么推
g i = g i − 1 ∗ i + ( i − 1 ) ! g_i=g_{i-1}*i+(i-1)! gi=gi1i+(i1)!

C o d e \mathcal{Code} 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;
}

如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值