码迷,mamicode.com
首页 > 其他好文 > 详细

[Usaco2011]Above the Median

时间:2020-07-04 11:55:05      阅读:67      评论:0      收藏:0      [点我收藏+]

标签:预处理   天才   ||   class   +=   n+1   数字   else   vol   

知识点前置

·树状数组

题面来源

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;
}

[Usaco2011]Above the Median

标签:预处理   天才   ||   class   +=   n+1   数字   else   vol   

原文地址:https://www.cnblogs.com/jyx-txzr/p/13234261.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!