P2309(逆序对)

传送门

描述:给定一个序列长n,求多少子串和大于零。

\(一开始一定会想到根据前缀和优化,枚举起点和中点O(n)解决\)

\(那更高效的方法呢?实际上,我们上面就是要求S_i-S_j>0的数量\)(S为前缀和数组)

是不是很眼熟?就是一个正序对嘛!归并排序搞一搞。(特殊的,序列长可以为0,另外算)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,s[100009],a[100009],b[100009];
ll ans;
void merge(int l,int mid,int r)
{
	int p1=l,p2=mid+1,k=l;
	while(p1<=mid&&p2<=r)
	{
		if(a[p1]<a[p2])	ans+=mid-p1+1,b[k++]=a[p2++];
		else	b[k++]=a[p1++];
	}
	while(p1<=mid)
		b[k++]=a[p1++];
	while(p2<=r)
		b[k++]=a[p2++];
	for(int i=l;i<=r;i++)	a[i]=b[i];
}
void mergesort(int l,int r)
{
	if(r>l)
	{
		int mid=l+r>>1;
		mergesort(l,mid);
		mergesort(mid+1,r);
		merge(l,mid,r);
	}
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)	cin>>s[i],a[i]=a[i-1]+s[i];
	for(int i=1;i<=n;i++)	if(a[i]>0)	ans++;
	mergesort(1,n);
	cout<<ans;
}

树状数组版本

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+9;
int sumn[maxn],sm[maxn],n,tot;
ll ans;
class Binary_Index_Tree
{
	private:
		int sumn[maxn];
		int lowbit(int x){
			return x&(-x);
		}
	public:
		void insert(int x)
		{
			for(;x<=tot;x+=lowbit(x))	sumn[x]++;
		}
		int query(int x)
		{
			int ans=0;
			for(;x;x-=lowbit(x))	ans+=sumn[x];
			return ans;
		}
}bit;
int main()
{
	cin>>n;
	for(int i=1,x;i<=n;i++)
	{
		scanf("%d",&x);
		sumn[i]=sumn[i-1]+x;
		sm[i]=sumn[i];
	}
	sort(sm+1,sm+1+n);
	tot=unique(sm+1,sm+1+n)-sm-1;
	for(int i=1;i<=n;i++)
	{
		int p=lower_bound(sm+1,sm+tot+1,sumn[i])-sm;
		ans+=bit.query(p-1);//搜集1到(p-1)数字
		bit.insert(p); 
	}
	for(int i=1;i<=n;i++)	if(sumn[i]>0)	ans++;
	cout<<ans;
}
posted @ 2020-04-20 15:26  倾叶子佮  阅读(185)  评论(0编辑  收藏  举报