Educational Codeforces Round 81 (Rated for Div. 2)F(线段树)

预处理把左集划分为大小为1~i-1时,把全部元素都移动到右集的代价,记作sum[i]。

然后枚举终态时左集的大小,更新把元素i 留在/移动到 左集的代价。

树状数组/线段树处理区间修改/区间查询

 1 #define HAVE_STRUCT_TIMESPEC
 2 #include<bits/stdc++.h>
 3 using namespace std;
 4 #define ll long long
 5 const int N=2e5+7;
 6 struct Tree{
 7     ll minn,lazy;
 8 }tree[N<<2];
 9 ll sum[N];//前缀和
10 inline void build(int root,int l,int r){
11     if(l==r){
12         tree[root].minn=sum[l];//1~l的a[i]之和
13         tree[root].lazy=0;
14         return;
15     }
16     int mid=(l+r)>>1;
17     build((root<<1),l,mid);
18     build((root<<1|1),mid+1,r);
19     tree[root].minn=min(tree[(root<<1)].minn,tree[(root<<1|1)].minn);//up
20     return;
21 }
22 inline void pushdown(int root){
23     if(!tree[root].lazy)
24         return;
25     tree[(root<<1)].minn+=tree[root].lazy;
26     tree[(root<<1|1)].minn+=tree[root].lazy;
27     tree[(root<<1)].lazy+=tree[root].lazy;
28     tree[(root<<1|1)].lazy+=tree[root].lazy;
29     tree[root].lazy=0;
30     return;
31 }
32 inline void change(int root,int l,int r,int x,int y,int val){
33     if(r<x||l>y)
34         return;
35     if(x<=l&&r<=y){
36         tree[root].minn+=val;
37         tree[root].lazy+=val;
38         return;
39     }
40     int mid=(l+r)>>1;
41     pushdown(root);
42     change((root<<1),l,mid,x,y,val);
43     change((root<<1|1),mid+1,r,x,y,val);
44     tree[root].minn=min(tree[(root<<1)].minn,tree[(root<<1|1)].minn);//up
45     return;
46 }
47 int n,p[N],a[N],pos[N];
48 ll ans;
49 int main(){
50     ios::sync_with_stdio(false);
51     cin.tie(NULL);
52     cout.tie(NULL);
53     cin>>n;
54     for(int i=1;i<=n;++i){
55         cin>>p[i];
56         pos[p[i]]=i;//数字p[i]出现的位置为i
57     }
58     for(int i=1;i<=n;++i){
59         cin>>a[i];
60         sum[i]=sum[i-1]+a[i];//sum[i]为左集合大小为i,把左集合所有元素都移动到右集合的花费
61     }
62     build(1,1,n-1);
63     ans=min(a[1],a[n]);//a[1]为左集为空,a[n]为右集为空
64     for(int i=1;i<n;++i){//枚举左集大小,定下大小后,集合内元素也被定为1~i
65         change(1,1,n-1,1,pos[i]-1,a[pos[i]]);//找到元素i出现的位置,在它出现位置左边的sum[i]分别加上把元素i从右集合移动到左集合的代价(原本的sum[1~i-1]为把原本处于位置1~i-1的元素都移动到右边,此时加上元素1~i从右移动到左的代价)
66         change(1,1,n-1,pos[i],n,-a[pos[i]]);//在它出现位置及其右边的sum[i]分别减去把元素i从左集合移动到右集合的代价(元素i无需移动,可是移动的代价事先已经加到sum[i~n]里了)
67         ans=min(ans,tree[1].minn);//如果左集大小为i的代价最小就更新最小值
68     }
69     cout<<ans;
70     return 0;
71 }

猜你喜欢

转载自www.cnblogs.com/ldudxy/p/12244494.html