n < = 2 e 5 n<=2e5 n<=2e5
设 f i f_i fi为删除 ( i , p i ) (i,p_i) (i,pi)的线段并使得不存在线段 ( a , b ) , a < i , b < p i (a,b),a<i,b<p_i (a,b),a<i,b<pi的最少花费。 那么容易发现可以转移到 f i f_i fi的 f j f_j fj一定满足 p j p_j pj是 k = i − 1 , i − 2.... j k=i-1,i-2....j k=i−1,i−2....j中最大的满足 p k < p i p_k<p_i pk<pi的 p k p_k pk。 也就是说在 k = i − 1 , i − 2....1 k=i-1,i-2....1 k=i−1,i−2....1且满足 p k < p i p_k<p_i pk<pi这个序列中 j j j是前缀最大值。 那么我们的问题就转化为了求前缀最大值的所有位置 j j j上的 f j f_j fj的最小值。 那么先从 i = 1 , 2.... n i=1,2....n i=1,2....n的顺序插入 ( i , p i ) (i,p_i) (i,pi),用一个李超线段树维护 p k < p i p_k<p_i pk<pi的 k k k所形成的序列的前缀最大值的位置上的 f f f的最小值。 解法类似于楼房重建
A C C o d e \rm AC\ Code AC Code
#include<bits/stdc++.h> #define maxn 200005 #define inf 0x7f7f7f7f #define lc u<<1 #define rc u<<1|1 using namespace std; int n,p[maxn],c[maxn],f[maxn]; struct data{ int a,c; data(int a=0,int c=inf):a(a),c(c){} data operator +(const data &B)const{ return B.a<=0?*this:data(B.a,min(B.c,c)); } }s[maxn<<2],sl[maxn<<2]; data cal(int u,int l,int r,int h){ if(l==r) return s[u].a>h?s[u]:data(); int mid = (l+r) >> 1; if(s[rc].a>h) return cal(rc,mid+1,r,h)+sl[u]; return cal(lc,l,mid,h); } void upd(int u,int l,int r){ s[u] = s[rc] + (sl[u] = cal(lc,l,(l+r)>>1,s[rc].a)); } void Insert(int u,int l,int r,int p,data v){ if(l==r){ s[u]=v;return; } int mid = (l+r) >> 1; if(p <= mid) Insert(lc,l,mid,p,v); else Insert(rc,mid+1,r,p,v); upd(u,l,r); } data d; void Query(int u,int l,int r,int ql,int qr){ if(ql>qr) return; if(ql<=l&&r<=qr){ d=d+cal(u,l,r,d.a); return;} int mid = (l+r) >> 1; if(qr>mid) Query(rc,mid+1,r,max(ql,mid+1),qr); if(ql<=mid) Query(lc,l,mid,ql,min(mid,qr)); } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&p[i]); for(int i=1;i<=n;i++) scanf("%d",&c[i]),f[i]=inf; f[1]=c[1],Insert(1,1,n,p[1],data(1,f[1])); for(int i=2;i<=n;i++){ d=data();Query(1,1,n,1,p[i]-1); int t = d.c;if(t >= inf) t = 0; Insert(1,1,n,p[i],data(i,f[i]=min(f[i],t+c[i]))); } d=data();Query(1,1,n,1,n); printf("%d\n",d.c); }考场上写的 C D Q \rm CDQ CDQ分治+ z k w \rm zkw zkw线段树。 主要就是按第一维分治求左半对右半的贡献, 按第二维 p p p从小到大枚举,左半能贡献的只有 p i < = p_i<= pi<=当前枚举的 p p p的所有 i i i中的后缀最大值,用线段树维护给右边,右边每一个点能接受的区间要看他前一个前缀最大值,故左右都需要用单调栈维护。
A C C o d e \rm AC \ Code AC Code
#include<bits/stdc++.h> #define maxn 200005 #define inf 0x7f7f7f7f #define lc u<<1 #define rc u<<1|1 using namespace std; int n,p[maxn],c[maxn],f[maxn]; struct data{ int a,c; data(int a=0,int c=inf):a(a),c(c){} data operator +(const data &B)const{ return B.a<=0?*this:data(B.a,min(B.c,c)); } }s[maxn<<2],sl[maxn<<2]; data cal(int u,int l,int r,int h){ if(l==r) return s[u].a>h?s[u]:data(); int mid = (l+r) >> 1; if(s[rc].a>h) return cal(rc,mid+1,r,h)+sl[u]; return cal(lc,l,mid,h); } void upd(int u,int l,int r){ s[u] = s[rc] + (sl[u] = cal(lc,l,(l+r)>>1,s[rc].a)); } void Insert(int u,int l,int r,int p,data v){ if(l==r){ s[u]=v;return; } int mid = (l+r) >> 1; if(p <= mid) Insert(lc,l,mid,p,v); else Insert(rc,mid+1,r,p,v); upd(u,l,r); } data d; void Query(int u,int l,int r,int ql,int qr){ if(ql>qr) return; if(ql<=l&&r<=qr){ d=d+cal(u,l,r,d.a); return;} int mid = (l+r) >> 1; if(qr>mid) Query(rc,mid+1,r,max(ql,mid+1),qr); if(ql<=mid) Query(lc,l,mid,ql,min(mid,qr)); } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&p[i]); for(int i=1;i<=n;i++) scanf("%d",&c[i]),f[i]=inf; f[1]=c[1],Insert(1,1,n,p[1],data(1,f[1])); for(int i=2;i<=n;i++){ d=data();Query(1,1,n,1,p[i]-1); int t = d.c;if(t >= inf) t = 0; Insert(1,1,n,p[i],data(i,f[i]=min(f[i],t+c[i]))); } d=data();Query(1,1,n,1,n); printf("%d\n",d.c); }