T6 Maximum Element (CF886E)
T6 Maximum Element (CF886E)
又一道超水黑题
CF传送门
洛谷传送门
题意简述
从前有一个叫
(太长了缩了一下QwQ)
int fast_max(int n,int a[]){
int ans=0,offset=0;
for(int i=0;i<n;++i)
if(ans<a[i]) ans=a[i],offset=0;
else{
offset++;
if(offset==k)return ans;
}
return ans;
}
废话不多说,求有多少长度为
数据范围
不保证
题解
思路
直接考虑出错的情况太难啦
不妨从反面考虑,正确的情况有多少
其实就是函数在
这样就可以枚举
还差一个问题,选出一些数放到
(这个问题只和元素间的相对大小有关,不妨把它们归结到 (如果你看不懂上面这段话请当我什么都没说))
总而言之,是个很明显的dp了
考虑
考虑最大值
反之,只要
所以用
别忘了要先从i-1个数中选出j-1个数排到max前面
max后面的数是可以随便放的
答案计算就很简单了
很遗憾
我们来把这个式子打开优化
很惊奇地发现
于是前缀和优化
当然ans的计算也可以这样优化(不过没什么必要只是为了好写)
做法
预处理阶乘逆元什么的
已经说得很清楚了吧?
注意事项
· 依旧是开long long
· 记得特判
· 注意初始化和特判
AC代码
#include<bits/stdc++.h>
using namespace std;
const long long N=1000005;
long long mod=1e9+7;
long long f[N],s[N],ans=0;
long long mul[N],inv[N];
long long ksm(long long a,long long x){
long long tot=1;
while(x){
if(x&1) tot=tot*a%mod;
a=a*a%mod,x>>=1;
}
return tot;
}
int main(){
long long n,k;
scanf("%lld%lld",&n,&k);
if(n<=k) {printf("0");return 0;}//特判
mul[1]=1;
for(long long i=2;i<=n;i++) mul[i]=mul[i-1]*i%mod;
inv[n]=ksm(mul[n],mod-2);
for(long long i=n-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%mod;//快速处理逆元
for(long long i=1;i<=k;i++) f[i]=mul[i],s[i]=(s[i-1]+f[i]*inv[i]%mod)%mod;
//当i<=k时,所有情况都不会退出
for(long long i=k+1;i<=n;i++){
f[i]=mul[i-1]*((s[i-1]+mod-s[i-k-1])%mod)%mod;//dp
s[i]=(s[i-1]+f[i]*inv[i]%mod)%mod;//(f[i]/i)前缀和
}
ans=mul[n-1];//n=1时,就是(n-1)!种,不可不计算
for(long long i=2;i<=n;i++)
ans=(ans+f[i-1]*mul[n-1]%mod*inv[i-1]%mod)%mod;//计算答案
printf("%lld",(mul[n]+mod-ans)%mod);//取补集
}