CF1295E Permutation Separation
线段树
难得把E想出来,写出来,但却没有调出来(再给我5分钟),我的紫名啊,我一场上紫的大好机会啊
首先考虑是否能将$k$在$1$--$n-1$的每一个的最小代价都求出来
因为$k$从$i$到$i-1$左右两边的集合只相差一个数,所以可以考虑递推
可以发现如果最终满足条件,那么左边集合的最大数一定为该集合当前的大小
且在每一个小于此大小的数都存在于左边这个集合中
由于给出的序列是一个排列,每一个数的范围是$1$--$n$
那么只要对于每一个k都进行枚举左边集合的大小计算代价,取最小值,就是该取该k时最小值
但直接暴力枚举的复杂度时$O(n^{2})$的
那么可以线段树维护以左边集合大小为每一个元素的序列
我们让$k$从$n-1$到$1$开始考虑递推
对于将$k$减一,左边集合只差了一个数
设这个数为$p$,代价为$v$
那么这个数对枚举左边集合大小的影响即是
集合大小为$p$-$n$的代价$+v$
集合大小为$0$-$p-1$的代价$-v$
然后线段树维护区间修改即可
1 #include <bits/stdc++.h> 2 #define m_k make_pair 3 #define int long long 4 #define inf (int)1e18 5 using namespace std; 6 const int N=2*1e5+100; 7 int n,p[N],v[N],sum[N],ans; 8 struct node 9 { 10 int MIN,lazy; 11 }sh[N*4]; 12 void pushup(int x) 13 { 14 sh[x].MIN=min(sh[x+x].MIN,sh[x+x+1].MIN); 15 } 16 void pushdown(int x) 17 { 18 int v=sh[x].lazy; 19 sh[x+x].lazy+=v; 20 sh[x+x].MIN+=v; 21 sh[x+x+1].lazy+=v; 22 sh[x+x+1].MIN+=v; 23 sh[x].lazy=0; 24 } 25 void build(int x,int l,int r) 26 { 27 if (l==r) 28 { 29 sh[x].lazy=0; 30 sh[x].MIN=sum[n]-sum[l];//此时将k取到n,所有元素都在左边集合 31 return; 32 } 33 int mid=(l+r)>>1; 34 build(x+x,l,mid); 35 build(x+x+1,mid+1,r); 36 pushup(x); 37 } 38 void change(int x,int l,int r,int L,int R,int v)//线段树维护区间修改 39 { 40 if (L<=l && R>=r) 41 { 42 sh[x].MIN+=v; 43 sh[x].lazy+=v; 44 return; 45 } 46 pushdown(x); 47 int mid=(l+r)>>1; 48 if (L<=mid) change(x+x,l,mid,L,R,v); 49 if (R>mid) change(x+x+1,mid+1,r,L,R,v); 50 pushup(x); 51 } 52 signed main() 53 { 54 scanf("%lld",&n); 55 for (int i=1;i<=n;i++) 56 scanf("%lld",&p[i]); 57 for (int i=1;i<=n;i++) 58 { 59 int a; 60 scanf("%lld",&a); 61 v[p[i]]=a;//记录每一个数值的代价 62 } 63 for (int i=1;i<=n;i++) sum[i]=sum[i-1]+v[i];//计算前缀和 64 build(1,0,n); 65 ans=inf;//由于k不能取到n,所有ans不进行计算,直接赋值为inf 66 for (int i=n;i>=2;i--) 67 { 68 change(1,0,n,p[i],n,v[p[i]]);//同上 69 change(1,0,n,0,p[i]-1,-v[p[i]]); 70 ans=min(ans,sh[1].MIN); 71 } 72 printf("%lld\n",ans); 73 }