【jzoj1967】【离散化】【树状数组】【普及模拟】数列

题目描述

给定一个长度为N的数列,求一段连续的子数列满足该子数列中的各元素的平均数大于A,输出可行方案的总数。

输入

第一行两个整数N,A
接下来N个整数,代表数列的N个元素。

输出

一个整数,即可行的方案数。

样例输入

5 1
1 2 3 4 5

样例输出

14

数据范围限制

数据规模
对于60%的数据 N <= 1000
对于100%的数据 N <= 100000
所有数据包括都在longint范围内。


解题思路

60分做法
求出前缀和,枚举数列的左端点和右端点,然后求出和看看大不大于A
注意:因为平均数可能是小数,所以要按小数求

100分做法
对于每一次的输入的数a[i],我们都将它减去平均数A,然后全部做一遍前缀和。
首先我们知道对于某个前缀和sum[i],如果它>0就先给答案+1
然后如果sum[i] < sum[j],i < j就代表在区间[l,r]内的总和是绝对比A大的,就累加1
然后我们怎么去处理sum[j]>sum[i]的情况呢,我们发现如果强行去枚举
时间复杂度就是O(Σ N),很明显会炸时间
然后我们可以想到树状数组,
先将处理出来的sum全部离散,因为离散后其的大小关系如sum[i] < sum[j]是不变的,然后离散后其的最坏情况最大值就是N,即100000,树状数组就只需要开到100000了,
然后枚举sum,
每次对于一个sum[i]
ans累加其树状数组中1~i-1的值,用x-x and (-x)去递减
然后将这个数丢进树状内
用x+x and (-x)去 递进,tree[x]=tree[x]+1


蒟蒻不才,只写出了60分 (我不会树状数组!!!)

#include<iostream>
#include<cstdio>
using namespace std;
long long Gun,s[100100],n,A;
int main(){
//	freopen("sequence.in","r",stdin);
//	freopen("sequence.out","w",stdout);
	scanf("%lld%lld",&n,&A);
	for(int i=1;i<=n;++i){
		scanf("%lld",&s[i]);
		s[i]+=s[i-1];//前缀和
	}
	for(int i=1;i<=n;++i)
	    for(int j=i;j<=n;++j)
	        if((s[j]-s[i-1])*1.0/(j-i+1)>A)//算平均值,看大不大于A
	           ++Gun;
	printf("%lld",Gun);
}

你可能感兴趣的:(离散化,树状数组)