题意
若一个1 ~ n的排列中存在一个区间
[
L
,
R
]
[L,R]
[L,R]使得这个区间内元素的最大值与最小值之差为
R
−
L
R-L
R−L,则称其为一个片段,显然所有长度为1的区间均为一个片段。现在给定n与一个素数m,求1 ~ n的全排列中所有片段数之和对m取模的结果。
1
≤
n
≤
250000
,
1
0
8
≤
m
≤
1
0
9
1≤n≤250000, 10^8≤m≤10^9
1≤n≤250000,108≤m≤109
思路
这题是个DP,乍一看没有什么想法简单暴力显然行不通,求出每个排列然后依次统计必然是TLE的。
所以我们换一个思路,求出每种长度的片段数,再计算一下对于每种长度的片段,它在所有
1
1
1 ~
n
n
n的排列中有多少种放法,把它们乘起来求和就搞定了。
具体来说,对于
1
1
1 ~
n
n
n的排列,长度为
i
i
i的片段,假设这个片段包含的元素为
X
X
X ~
X
+
i
−
1
X+i-1
X+i−1:
- 这个片段自身有 i ! i! i!种排列方式
- 剩下有 n − i n-i n−i个数,则这个片段可以放置在 ( n − i + 1 ) (n-i+1) (n−i+1)个不同的位置(在它前面分别有0,1,…, n − i n-i n−i个数)
- 剩下的 n − i n-i n−i个数有 ( n − i ) ! (n-i)! (n−i)!种排列方式
- L的取值有
1
1
1 ~
(
n
−
i
+
1
)
(n-i+1)
(n−i+1) 共
(
n
−
i
+
1
)
(n-i+1)
(n−i+1)种
所以对于长度为 i i i的片段,总共有 i ! ∗ ( n − i ) ! ∗ ( n − i + 1 ) ∗ ( n − i + 1 ) i!*(n-i)!*(n-i+1)*(n-i+1) i!∗(n−i)!∗(n−i+1)∗(n−i+1)种放置方式,枚举考虑 i = 1 , 2...... , n i=1,2......,n i=1,2......,n的情况求和即可。
然后我因为忘记取模wa了3发
保险起见,最好在每次乘积之后都进行一次取模运算。
AC代码
#include<stdio.h>
long long m,n;
long long mu[250005];
long long ans;
int main()
{
scanf("%I64d%I64d",&n,&m);
mu[0]=1;
for(int i=1;i<=250000;i++)
{mu[i]=mu[i-1]*(long long)i;mu[i]%=m;};
for(int i=1;i<=n;i++)
{
ans+=((((mu[i]*mu[n-i]%m)*(n-((long long)i-1))%m)*(n-((long long)i-1)))%m);
ans%=m;
}
printf("%I64d",ans);
return 0;
}