https://vjudge.net/problem/CodeForces-1295E
題目大意:爲的某個全排列,爲移動所需要的花費,你可以選取任意,把序列分成兩部分,即和。然後你可以做如下操作,即每次從任意一個序列中選取某個元素移動到另一個序列中,最終你需要使得第一個序列中的任意一個元素小於第二個序列中的任意一個元素(即前者中的最大值小於後者中的最小值),問最小花費是多少(如果這兩個序列中任意一個序列爲空,則認爲該方案也符合題意)。
思路:主要是轉換這一步比較難想。設值滿足第一個序列中的元素,第二個序列中的元素,由於是的全排列,那麼的取值範圍爲(包含了某個序列爲空的情況)。假設爲一定值,表示選取時所需的花費,那麼顯然由兩部分組成:
很明顯,當爲定值時,可通過遞推計算出,複雜度爲。然而,所以暴力的複雜度爲,顯然不行。那麼我們思考一下當增加時,數組的變化情況,首先看下圖:
不難發現,當增加時,第一部分中值爲的元素不用移到第二部分了,且第二部分中值爲的元素需要移到第一部分,不妨設,那麼也就是說的花費可以減少,的花費需要增加,而我們所求的最優解,區間修改、查詢最小值……不就是線段樹模板題嗎!
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
struct Tree
{
int l,r;
ll lazy,MIN;
}tree[maxn<<2];
int n;
int a[maxn],p[maxn],idx[maxn];
ll t[maxn];
inline void up(int i)
{
tree[i].MIN=min(tree[i<<1].MIN,tree[i<<1|1].MIN);
}
inline void down(int i)
{
int l=i<<1,r=i<<1|1;
tree[l].lazy+=tree[i].lazy,tree[r].lazy+=tree[i].lazy;
tree[l].MIN+=tree[i].lazy,tree[r].MIN+=tree[i].lazy;
tree[i].lazy=0;
}
void build(int i,int l,int r)
{
tree[i].l=l,tree[i].r=r,tree[i].lazy=0;
if(l==r)
{
tree[i].MIN=t[l];
return ;
}
int mid=l+r>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
up(i);
}
void update(int i,int l,int r,int v)
{
if(tree[i].l==l&&tree[i].r==r)
{
tree[i].MIN+=v,tree[i].lazy+=v;
return ;
}
if(tree[i].lazy)
down(i);
int mid=tree[i].l+tree[i].r>>1;
if(mid>=r)
update(i<<1,l,r,v);
else if(mid<l)
update(i<<1|1,l,r,v);
else
update(i<<1,l,mid,v),update(i<<1|1,mid+1,r,v);
up(i);
}
ll query(int i,int l,int r)
{
if(tree[i].l==l&&tree[i].r==r)
return tree[i].MIN;
if(tree[i].lazy)
down(i);
int mid=tree[i].l+tree[i].r>>1;
if(mid>=r)
return query(i<<1,l,r);
else if(mid<l)
return query(i<<1|1,l,r);
else
return min(query(i<<1,l,mid),query(i<<1|1,mid+1,r));
up(i);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&p[i]),idx[p[i]]=i;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
t[i]=t[i-1]+a[i];
}
build(1,1,n);
int val=1,id;
ll ans=query(1,1,n-1);
while(val<=n)
{
id=idx[val];
update(1,id,n,-a[id]);
if(id>1)
update(1,1,id-1,a[id]);
ans=min(ans,query(1,1,n-1));
++val;
}
printf("%lld\n",ans);
return 0;
}