洛谷P3031 中石油2557 Above the Median (数组模拟or树状数组求逆序数)

Above the Median

题目描述

Farmer John has lined up his N (1 <= N <= 100,000) cows in a row to measure their heights; cow i has height H_i (1 <= H_i <= 1,000,000,000) nanometers--FJ believes in precise measurements! He wants to take a picture of some contiguous subsequence of the cows to submit to a bovine photography contest at the county fair.

The fair has a very strange rule about all submitted photos: a photograph is only valid to submit if it depicts a group of cows whose median height is at least a certain threshold X (1 <= X <= 1,000,000,000).

For purposes of this problem, we define the median of an array A[0...K] to be A[ceiling(K/2)] after A is sorted, where ceiling(K/2) gives K/2 rounded up to the nearest integer (or K/2 itself, it K/2 is an integer to begin with). For example the median of {7, 3, 2, 6} is 6, and the median of {5, 4, 8} is 5.

Please help FJ count the number of different contiguous subsequences of his cows that he could potentially submit to the photography contest.

给出一串数字,问中位数大于等于X的连续子串有几个。(这里如果有偶数个数,定义为偏大的那一个而非中间取平均)

输入输出格式

输入格式:

  • Line 1: Two space-separated integers: N and X.

  • Lines 2..N+1: Line i+1 contains the single integer H_i.

输出格式:

  • Line 1: The number of subsequences of FJ's cows that have median at least X. Note this may not fit into a 32-bit integer.

输入输出样例

输入样例#1:  复制
4 6 
10 
5 
6 
2 
输出样例#1: 复制
7 









说明

FJ's four cows have heights 10, 5, 6, 2. We want to know how many contiguous subsequences have median at least 6.

There are 10 possible contiguous subsequences to consider. Of these, only 7 have median at least 6. They are {10}, {6}, {10, 5}, {5, 6}, {6, 2}, {10, 5, 6}, {10, 5, 6, 2}.

求一组相邻数的中位数大于等于k的子串个数,若有偶数个数,则取较大那个数为中位数。

首先考虑一下能不能模拟出来,洛谷题解里面有人写的很详细,就是用一个数组记录前缀和,一个数组记录当前前缀出现的次数,然后类似于DP的做法模拟。需要注意的是把比k小的数看作-1,其他数看作+1,这样的话当pre数组为正时,说明大于k的为占总数的一半及以上,具体解释看代码实现,耗时O(n).超快。

代码实现:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<cstdio>
#define ll long long
#define mset(a,x) memset(a,x,sizeof(a))

using namespace std;
const double PI=acos(-1);
const int inf=0x3f3f3f3f;
const double esp=1e-6;
const int maxn=1e6+5;
const int mod=1e9+7;
int dir[4][2]={0,1,1,0,0,-1,-1,0};
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
ll inv(ll b){if(b==1)return 1; return (mod-mod/b)*inv(mod%b)%mod;}
ll fpow(ll n,ll k){ll r=1;for(;k;k>>=1){if(k&1)r=r*n%mod;n=n*n%mod;}return r;}
ll pre[maxn],vis[maxn];

int main()
{
	ll n,i,j,k,ans,x;
	while(cin>>n>>k)
	{
		mset(vis,0);
		for(i=1;i<=n;i++)         //记录前缀和 
		{
			cin>>x;
			if(x>=k)
			pre[i]=pre[i-1]+1;
			else
			pre[i]=pre[i-1]-1;
		}
		ll temp=0;               //记录前i-1的状态下满足条件的区间个数 
		vis[0]=1;                //记录前缀和出现的次数 
		pre[0]=0;                //前缀0初始化为0 
		ans=0;
		for(i=1;i<=n;i++)        //枚举1-n 
		{
			if(pre[i]>pre[i-1])         //如果pre[i]>pre[i-1]以第i个数结尾的合法区间数 
			temp+=vis[pre[i]]+1;        //前面的前缀和加上此前缀出现的次数,由于两者最多差1,所以加上1 
			else
			temp-=vis[pre[i-1]]-1;      //同理,减去1 
			
			vis[pre[i]]++;
			ans+=temp; 
		}
		cout<<ans<<endl; 
	}
	return 0;
}


如果用点普遍且高级的算法实现的话,那就是树状数组了。跟数组模拟的预处理相同的是得到前缀数组,然后就会发现这就是简单的求逆序对的数量啊,位置也没改变,数的大小也没改变。还是有需要注意的地方,就是前缀和可能出现负数或者是0,我们不妨在建树的时候统一加上n+1.这样需要先跑一遍数组,把所有值都更新为n+1.然后再往里面加数,最后求的逆序对数量。

代码实现:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<cstdio>
#define ll long long
#define mset(a,x) memset(a,x,sizeof(a))

using namespace std;
const double PI=acos(-1);
const int inf=0x3f3f3f3f;
const double esp=1e-6;
const int maxn=1e6+5;
const int mod=1e9+7;
int dir[4][2]={0,1,1,0,0,-1,-1,0};
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
ll inv(ll b){if(b==1)return 1; return (mod-mod/b)*inv(mod%b)%mod;}
ll fpow(ll n,ll k){ll r=1;for(;k;k>>=1){if(k&1)r=r*n%mod;n=n*n%mod;}return r;}
ll n,vis[maxn],sum[maxn];

ll lowbit(ll x)
{
	return x&(-x);
}

void add(ll x,ll value)
{
	ll i;
	for(i=x;i<=2*n+1;i+=lowbit(i))
	{
		sum[i]+=value;
	}
}

ll getsum(ll x)
{
	ll i,ans=0;
	for(i=x;i>0;i-=lowbit(i))
	{
		ans+=sum[i];
	}
	return ans;
}

int main()
{
	ll i,j,k,x;
	while(~scanf("%lld%lld",&n,&k))
	{
		mset(vis,0);
		for(i=1;i<=n;i++)
		{
			scanf("%lld",&x);
			if(x>=k)
			vis[i]=vis[i-1]+1; 
			else
			vis[i]=vis[i-1]-1; 
		}
		ll ans=0;
		mset(sum,0);
		add(n+1,1);
		for(i=1;i<=n;i++)
		{
			ans+=getsum(vis[i]+n+1);
			add(vis[i]+n+1,1); 
		}
		cout<<ans<<endl;
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值