https://codeforces.com/contest/1295/problem/E
状态下降有点厉害,水题花了挺久的时间,而且线段树tag+=写成=,LL x=tag写成int x=tag,浪费了不少时间
我们可以很容易地想到这题有两个变量,一个是在哪里截断,一个是最后要让哪些元素在左集合,哪些元素在右集合。
那么我们可以想到维护一个这样的答案ans[x],在当前的截断情况下,如果让x到n都在右集合,1-x-1在左集合,答案是多少。
我们发现可以使用线段树维护一个这样的ans[1]---ans[n],然后每次截断往右移动一位,更新一下就行了,区间修改,维护区间最小值。
特殊情况,全放左边或者全放右边,所以初始ans=min(a[1],a[n])
#include<bits/stdc++.h>
using namespace std;
const int maxl=2e5+10;
int n;
long long ans;
int a[maxl],val[maxl],p[maxl];
struct node
{
int l,r;
long long tag,mi;
}tree[maxl<<2];
inline void build(int k,int l,int r)
{
tree[k].l=l;tree[k].r=r;tree[k].tag=tree[k].mi=0;
if(l==r)
return;
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
inline void prework()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&p[i]);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
val[p[i]]=a[i];
}
build(1,1,n+1);
}
inline void gank(int k)
{
long long x=tree[k].tag;
if(x==0 || tree[k].l==tree[k].r)
return;
tree[k<<1].tag+=x;tree[k<<1].mi+=x;
tree[k<<1|1].tag+=x;tree[k<<1|1].mi+=x;
tree[k].tag=0;
}
inline void add(int k,int l,int r,int x)
{
gank(k);
if(tree[k].l==l && tree[k].r==r)
{
tree[k].mi+=x;tree[k].tag+=x;
return;
}
int mid=(tree[k].l+tree[k].r)>>1;
if(r<=mid)
add(k<<1,l,r,x);
else if(l>mid)
add(k<<1|1,l,r,x);
else
add(k<<1,l,mid,x),add(k<<1|1,mid+1,r,x);
tree[k].mi=min(tree[k<<1].mi,tree[k<<1|1].mi);
}
inline void mainwork()
{
ans=min(a[1],a[n]);
add(1,1,p[1],a[1]);
for(int i=2;i<=n;i++)
add(1,p[i]+1,n+1,a[i]);
ans=min(ans,tree[1].mi);
for(int i=2;i<n;i++)
{
add(1,p[i]+1,n+1,-a[i]);
add(1,1,p[i],a[i]);
ans=min(ans,tree[1].mi);
}
}
inline void print()
{
printf("%lld",ans);
}
int main()
{
prework();
mainwork();
print();
return 0;
}