习题:Permutation Separation(线段树)

题目

传送门

思路

考虑暴力,如果我们随便将其分成两块,因为一个序列的所有数要小于另一个序列的所有数,那么有一个序列一定是1~s,时间复杂度\(O(n^2)\)

考虑对其进行进一步的优化,我们假设线段树的第i个叶子结点表示第一个序列是1~i的最小值,那么答案就是区间最小值

还是枚举分界的区间,考虑分解的区间向右移一位会发生什么,

也就是\(p_{i+1}\)从大的序列移到了小的序列,那么\(1到p_{i+1}-1\)的所有区间都必须加上\(a_{i+1}\)

与之相对的\(p_{i+1}到n\)就需要减去\(a_{i+1}\)

代码

#include<iostream>
using namespace std;
namespace lst
{
	struct node
	{
		int l;
		int r;
		long long lazy,minn;
	}tre[800005];
	void build(int l,int r,int k)
	{
		tre[k].l=l;
		tre[k].r=r;
		if(l==r)
			return;
		int mid=(l+r)>>1;
		build(l,mid,k<<1);
		build(mid+1,r,k<<1|1);
	}
	void push_down(int k)
	{
		tre[k<<1].minn+=tre[k].lazy;
		tre[k<<1].lazy+=tre[k].lazy;
		tre[k<<1|1].minn+=tre[k].lazy;
		tre[k<<1|1].lazy+=tre[k].lazy;
		tre[k].lazy=0;
	}
	void add(int l,int r,long long val,int k)
	{
		if(l>tre[k].r||tre[k].l>r)
			return;
		if(l<=tre[k].l&&tre[k].r<=r)
		{
			tre[k].minn+=val;
			tre[k].lazy+=val;
			return;
		}
		push_down(k);
		add(l,r,val,k<<1);
		add(l,r,val,k<<1|1);
		tre[k].minn=min(tre[k<<1].minn,tre[k<<1|1].minn);
	}
}
using namespace lst;
int n;
long long p[200005];
long long a[200005];
long long ans=(1ll<<60);
int main()
{
	cin>>n;
	build(0,n,1);
	for(int i=1;i<=n;i++)
		cin>>p[i];
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=1;i<=n;i++)
		add(p[i],n,a[i],1);
	for(int i=1;i<n;i++)
	{
		add(p[i],n,-a[i],1);
		add(0,p[i]-1,a[i],1);
		ans=min(ans,tre[1].minn);
	}
	cout<<ans;
	return 0;
}
posted @ 2020-08-24 15:01  loney_s  阅读(91)  评论(0)    收藏  举报