[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\n",ans);
    return 0;
}
posted @ 2020-07-04 11:17  蒟蒻JYX  阅读(94)  评论(0编辑  收藏  举报