洛谷8月月赛Div1.C
infinities · · 题解
upd:修改最后一个式子的一个小错误
挖掘一下题意。
就是给定一个长度为
可以发现,因为枚举全排列中的每一个
接着考虑在什么情况下,滑动窗口在排列上从左往右滑动
因为向右滑动
对于最左边的数是窗口滑动之前是窗口最小值,进行一波推式子之后,可以发现它对答案的贡献有:
其中从左往右,求和符合是枚举这个最小值的大小,组合数是令窗口中剩余的
然后对于新进入的最右边的数是新的窗口最小值,式子是可以类比的:
含义是类似的,只不过计算的是新加进去最右边那个数。
当然,只将这两部分加起来还有一些问题,实际上我们还会算重一部分,既满足最左边是窗口最小值,又满足新加入的是窗口的新最小值(且小于最左边的,或者你可以强制令最右边的是最小的,最左边的小于最右边的,实际上两种方法的式子是一样的),这部分答案需要减掉:
前面同样是类似的,最后的
注意最后还需要加上每个排列都缺少的
于是简单线性预处理一下组合数,套一下式子就可以了。
核心代码:
for(int i = 1; i <= n - k; i++){//枚举只到n-k是因为i再大答案就都是0了
ans += C(n - i, k - 1) * (n - k) % mod * A[n - k] % mod * A[k - 1] % mod;//A是阶乘
ans %= mod;
ans += A[n - k - 1] * C(n - i, k) % mod * (n - k) % mod * A[k] % mod;
ans %= mod;
ans -= C(n - i, k - 1) * (n - k) % mod * A[n - k - 1] % mod * (i - 1) % mod * A[k - 1] % mod;
ans %= mod;
}
cout << ((A[n] + ans + mod) % mod + mod) % mod;