习题: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;
}

浙公网安备 33010602011771号