门票[求逆序对][前缀和数组][树状数组][离散化][归并排序]

门票

原题链接

https://matiji.net/exam/brushquestion/240/3181/1DC60EA6DF83A333301CFFE1407FBA59

题目

小码哥家附近的一个主题公园最近搞了一个活动,小码哥在活动中获得了一等奖。而一等奖是一长串的纸条,上面有n个格子,每个格子里有一个数字,活动人员告诉小码哥,只要他裁下一段连续的格子,格子内数字的总和/裁下的格子的数量大于等于门票费t,那么小码哥就能用这段格子抵消门票费

小码哥想要尽可能多的利用这个奖品,为此他想先知道有多少种裁剪的方案能裁出一段能抵消门票费的格子

由于方案数可能很大所以需要你输出方案数对1e9+7取模的结果

格式
输入格式:

第一行一个正整数n,t,表示格子的数量和门票费

第二行n个整数ai,表示格子内的数字

输出格式:

一个整数,表示总方案数对1e9+7取模的结果

样例 1
输入:
5 4 3 4 2 3 5
复制
输出:
3
复制
备注

1≤n≤106,1≤t≤108,0≤ai≤2∗1081≤n≤10^6,1≤t≤10^8,0≤ai≤2*10^81n1061t1080ai2108

解题思路(前缀和+离散化+树状数组)、(前缀和+归并排序)

​ 题意:在一个数组中找出所有平均数大于等于t的区间个数

前缀和数组可以快速查询区间和,我们用前缀和数组存储数组。设 s u m i sum_i sumi表示数组从 1 1 1 i i i 的前缀和,则区间 [ i + 1 , j ] [i+1,j] [i+1,j]的平均数为
s u m j − s u m i j − i \frac{sum_j-sum_i}{j-i} jisumjsumi

则 有 : s u m j − s u m i j − i > = t          ( j > = i ) 则有: \frac{sum_j-sum_i}{j-i}>=t\space\space\space\space\space\space\space\space(j>=i) jisumjsumi>=t        (j>=i)

整 理 得 : s u m j − t × j > = s u m i − t × i          ( j > = i ) 整理得:sum_j-t \times j>=sum_i-t \times i\space\space\space\space\space\space\space\space(j>=i) sumjt×j>=sumit×i        (j>=i)

所以我们只需要每个 j j j,我们只需要找到满足 i < = j i<=j i<=j s u m j − t ∗ j > = s u m i − t ∗ i sum_j-t*j >= sum_i-t*i sumjtj>=sumiti i i i的个数即可。我们将其看成一个新的数组 b b b,于是问题就转化成了求 b b b数组的非逆序对的个数,我们求出逆序对的个数,然后用总 数对 的个数减去逆序对的个数就可以得到答案。

而求逆序对常见的有两种方法:1、归并排序。2、树状数组。

对于本题来说,归并排序更好一些,因为用树状数组的话还需要进行离散化,而离散化本身又要用到排序。所以归并排序的时间复杂度更优,且不用离散化,代码更简单。

此外,能用树状数组解决的问题都能用线段树解决,此处不再赘述。

离散化+树状数组

树状数组。这里 s u m i − t ∗ i sum_i-t*i sumiti的值的范围很大,所以我们还需要采用离散化的方法。

对这些算法不了解的可以看看:

前缀和 https://oi-wiki.org/basic/prefix-sum/

树状数组 https://oi-wiki.org/ds/fenwick/

离散化 https://oi-wiki.org/misc/discrete/

归并排序

归并排序求解逆序对的个数只需要在归并排序的代码中加上一条语句。

有关归并排序的内容,请读者自行查阅资料。

归并排序 https://oi-wiki.org/basic/merge-sort/

示例代码

前缀和+离散化+树状数组

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+100;
const ll mod=1e9+7;
ll a[N],b[N],c[N];
ll tree[N];//树状数组
ll lowbit(ll x){
	return x&(-x);
}
ll sum(ll x){
	ll sum=0;
	while(x){
		sum+=tree[x];
		x-=lowbit(x);
	}
	return sum;
}
void add(ll x){
	while(x<N){
		tree[x]++;
		x+=lowbit(x);
	}
}
int main(){
	ll n,t;
	cin>>n>>t;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		a[i]+=a[i-1];
	}
	for(int i=1;i<=n;i++){
		b[i]=a[i]-i*t;
		c[i]=b[i];
	}
	sort(c,c+n+1);//离散化
	ll ans=0;
	for(int i=0;i<=n;i++){
		ll pos=lower_bound(c,c+n+1,b[i])-c+1;
		ans+=sum(pos);
		add(pos);
		ans%=mod;
	}
	cout<<ans<<endl;
	return 0;
}

前缀和+归并排序

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+10; 
const int mod=1e9+7;
ll n,t;
ll a[N],b[N];
ll ans;
void MergeSort(int l,int r){
	if(l==r) return;
	int mid=l+r>>1;
	MergeSort(l,mid);
	MergeSort(mid+1,r);
	int i=l,j=mid+1;
	for(int k=l;k<=r;k++){
		if(j>r||i<=mid&&a[i]<=a[j]) b[k]=a[i++];
		else{
			ans+=mid-i+1;//求逆序对的代码
			b[k]=a[j++];
		}
	}
	for(int k=l;k<=r;k++) a[k]=b[k];
}
int main(){
	ios::sync_with_stdio(false);
	cin>>n>>t;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		a[i]+=a[i-1];
	}
	for(int i=1;i<=n;i++){
		a[i]-=t*i;
	}
	MergeSort(0,n);//归并排序求逆序对
	ans=n*(n+1)/2-ans;//用总 数对 个数减去逆序对的个数
	ans%=mod;
	cout<<ans<<endl;
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值