CF1840C
CF1840C题解
题目描述
$T$ 组数据,每组数据给定 $n$,$k$,$q$ 和一个长度为 $n$ 的数组 $a$,求 $a$ 中长度大于等于 $k$ 且最大值小于等于 $q$ 的序列个数。
$\sum{n}\le 2e5$。
题目解析
- 解法一:数据结构解法
显然可以利用数据结构维护。考虑ST表预处理出区间最大值枚举区间左右端点累计,复杂度 $O(nlogn+n^2)$,需要优化。
再想想,若区间 $[i,j]$ 符合条件,则对于每个 $i\le k\le j$,区间 $[i,k]$ 都符合条件;若区间 $[i,j]$ 不符合,则对于每个 $j<k\le n$,区间 $[i,k]$ 都不符合,答案可二分。所以我们枚举左端点,二分右端点,复杂度 $O(Tnlogn)$。
- 解法二:数学解法
显然:一个区间符合条件,当且仅当此区间不存在一个 $i\in [i,j]$ 使 $a_i>q$。所以处理每一个 $a_i>q$ 的 $i$,统计区间长度。区间长度为 $m$ 的合法区间贡献即为 $\sum_{i=1}^{m}i$。而这个式子可以预处理,复杂度 $O(N+Tn)$。
代码实现
因为 $\sum{n}\le 2e5$,所以答案需要 long long。
- 解法一
using namespace std;
const int N=2e5+10;
int T;
int n,k,q;
ll f[N][31];
inline long long maxx(int l,int r){
int len=log(r-l+1)/log(2);
return max(f[l][len],f[r-(1<<len)+1][len]);
}
int main(){
scanf("%d",&T);
while(T--){
ll ans=0;
scanf("%d%d%d",&n,&k,&q);
for(int i=1;i<=n;++i) scanf("%lld",&f[i][0]);
for(int i=1;i<=30;++i)
for(int j=1;j+(1<<(i-1))-1<=n;++j)
f[j][i]=max(f[j][i-1],f[j+(1<<(i-1))][i-1]);
for(int i=1;i+k-1<=n;++i){
int l=i+k-1,r=n;
while(l<r){
int mid=(l+r+1)>>1;
if(maxx(i,mid)<=q) l=mid;
else r=mid-1;
}
if(maxx(i,l)<=q) ans+=min(l,n)-i-k+2;
}
printf("%lld\n",ans);
}
return 0;
} - 解法二
using namespace std;
const int N=2e5+10;
int T;
int n,k,q;
ll a[N],sum[N];
int main(){
scanf("%d",&T);
for(int i=1;i<=N-10;++i) sum[i]=sum[i-1]+i;
while(T--){
ll ans=0,p=0;
scanf("%d%d%d",&n,&k,&q);
for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
for(int i=1;i<=n;++i){
if(a[i]>q){
if(p>=k) ans+=sum[p-k+1];
p=0;
}
else ++p;
}
if(p>=k) ans+=sum[p-k+1];
printf("%lld\n",ans);
}
}
完结撒花qwq
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 kketchup的小窝!