[Usaco2011]Above the Median

知识点前置

·树状数组

题面来源

https://www.luogu.com.cn/problem/P3031

题目大意

给你一个长度为 (n) 序列,求出满足以下条件的子序列个数有多少个:
中位数大于给出的 (k)

解题方法

自己想了半天才打出来
--------------------分割线--------------------
因为我们要求的区间中,每个数 (a_i)对于 (k)来说,只有两种情况:

[a_i>=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
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/jyx-txzr/p/13234261.html