https://codeforces.com/contest/1295/problem/E
建一颗线段树,叶子结点是花费从1到i所需要花费的前缀和,表示前i个元素全部移动到右边的花费,再维护区间最小值,然后从1到n-1扫一遍,对于第i个位置,找到数字i在序列中的位置 pos ,将区间1到pos-1加上数字i移动的花费,pos到n-1减去数字i移动的花费,因为位置大于等于i 的时候,不需要将数字i移动到右边,位置小于i 时,需要把数字i移动到左边,所以需要增加数字i的花费,结合线段树维护的是前缀和数组,那么对于每一个位置i 用线段树维护的最小值去更新答案ans即可。
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 using namespace std; 4 const int maxn = 2e5+5; 5 ll pos[maxn],cost[maxn],a[maxn],sum[maxn]; 6 struct segT{ 7 ll l,r; 8 ll dat,lz; 9 }t[maxn*4]; 10 void build(ll p,ll l,ll r){ 11 t[p].l = l,t[p].r = r; 12 if(l == r) { t[p].dat = sum[l] ,t[p].lz = 0;return;} 13 ll mid = (l+r)/2; 14 build(p*2,l,mid); 15 build(p*2+1,mid+1,r); 16 t[p].dat = min(t[p*2].dat ,t[p*2+1].dat ); 17 } 18 void upd(ll p,ll L,ll R,ll v){ 19 if(t[p].l >= L&&t[p].r <= R ) {t[p].dat += v,t[p].lz +=v;return;} 20 if (t[p].lz && L!=R ) { 21 t[p * 2].dat += t[p].lz ,t[p*2+1].dat +=t[p].lz ; 22 t[p * 2].lz += t[p].lz ,t[p*2+1].lz += t[p].lz; 23 t[p].lz = 0; 24 } 25 int mid = (t[p].l + t[p].r )/2; 26 if(L<=mid) upd(p*2,L,R,v); 27 if(R>mid) upd(p*2+1,L,R,v); 28 t[p].dat = min(t[p*2].dat ,t[p*2+1].dat ); 29 } 30 int query(ll p,ll l,ll r){ 31 if(l<=t[p].l && r>=t[p].r ) return t[p].dat ; 32 int mid = (t[p].l + t[p].r )/2; 33 int val = 0x3f3f3f3f; 34 if(l<=mid) val = min(val,query(p*2,l,r)); 35 if(r>mid) val = min(val,query(p*2+1,l,r)); 36 return val; 37 } 38 int main() 39 { 40 int n; 41 scanf("%d",&n); 42 for(int i = 1;i<=n;i++) { 43 scanf("%lld",&a[i]); 44 pos[a[i]] = i; 45 } 46 for(int i = 1;i<=n;i++){ 47 scanf("%lld",&cost[i]); 48 sum[i] = sum[i-1] + cost[i]; 49 } 50 build(1,1,n-1); 51 ll ans = cost[n]; 52 ans = min(ans,t[1].dat ); 53 for(int i = 1;i<=n;i++){ 54 if(pos[i]!=n) upd(1,pos[i],n-1,-cost[pos[i]]); 55 if(pos[i]!=1) upd(1,1,pos[i]-1,cost[pos[i]]); 56 ans = min(ans,t[1].dat); 57 } 58 printf("%lld",ans); 59 return 0; 60 }