标签:预处理 天才 || class += n+1 数字 else vol
·树状数组
https://www.luogu.com.cn/problem/P3031
给你一个长度为 \(n\) 序列,求出满足以下条件的子序列个数有多少个:
中位数大于给出的 \(k\)
自己想了半天才打出来
--------------------分割线--------------------
因为我们要求的区间中,每个数 \(a_i\)对于 \(k\)来说,只有两种情况:
那么,我们把\(a_i>=k\)这个位置打成1,而\(a_i<k\)打成-1,那么就变成了这样(k=6时):
num | 10 | 5 | 6 | 2 |
---|---|---|---|---|
a | 1 | -1 | 1 | -1 |
然后我们再把这个a丢到树状数组里去做前缀和,就得到了
sum | 1 | 0 | 1 | 0 |
---|
这个时候,我们就可以一遍一遍的查在\(i\)这个位置之前有多少个数字sum比sum[i]小,最后整合相加得到答案
注意:前缀和sum可能为负数,所以在搜的时候,把每个位置保险起见加上一个n+1,不影响结果
#include<bits/stdc++.h>
using namespace std;
const int maxn=200005;//maxn开两倍
long long n,x,ans;
long long a[maxn],num[maxn],sum[maxn];
void add(int x,int vol)
{
while(x<=n+n+1)
{
sum[x]+=vol;
x+=(x & -x);
}
}
int query(int x)
{
int ret=0;
while(x)
{
ret+=sum[x];
x-=(x & -x);
}
return ret;
}
int main()
{
scanf("%lld%lld",&n,&x);
a[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&num[i]);
if(num[i]>=x)a[i]=a[i-1]+1;
else a[i]=a[i-1]-1;
}
add(n+1,1);//预处理0的位置
for(int i=1;i<=n;i++)
{
ans+=query(a[i]+n+1);
add(a[i]+n+1,1);
}
printf("%lld\n",ans);
return 0;
}
标签:预处理 天才 || class += n+1 数字 else vol
原文地址:https://www.cnblogs.com/jyx-txzr/p/13234261.html