題目
正解
神仙題。
先考慮的情況怎麼做。爲了方便記
考慮一段一段這樣選,設狀態表示搞完前個,選什麼,這時候的最小代價。
轉移爲(另一個同理)
其中
顯然這個東西可以拆成前綴和。
考慮轉移對的限制。
設,表示選時,內的數都要選(也就是作爲最大值/最小值)。
(注意這個狀態跟題解中所說的是相反的)
於是顯然有:對於任意有
拆開考慮,和
發現這兩個都可以用單調棧維護,而且前者的分佈是離散的,後者的分佈是連續的。
我們要求兩個單調棧可以貢獻的部分的交集的貢獻。
將前者丟入線段樹中,然後對於後者,二分出可以貢獻的區間,將這個區間丟到線段樹上查。
時間複雜度是
值域更大的時候怎麼做?
考慮整體二分,然後硬點每個數只能選和。
做完之後,發現選的數,它實際上選的數小於等於。
選的數,它實際上選的數大於。
繼續二分下去即可。
代碼
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 100010
#define INF 10000000000ll
#define ll long long
int n,m;
int a[N],mx,b[N];
int p[N];
int oL[N][2],oR[N][2];
int L[N][2],R[N][2];
void getmin(int &a,int b){a=min(a,b);}
void getmax(int &a,int b){a=max(a,b);}
int abs(int a){return a>=0?a:-a;}
struct Segment_Tree{
int n;
ll t[N*4];
int id[N*4];
void Tbuild(int k,int l,int r){
t[k]=INF,id[k]=-1;
if (l==r) return;
int mid=l+r>>1;
Tbuild(k<<1,l,mid);
Tbuild(k<<1|1,mid+1,r);
}
void Tchange(int k,int l,int r,int x,ll c){
if (l==r){
t[k]=c;
id[k]=(c==INF?-1:x);
return;
}
int mid=l+r>>1;
if (x<=mid)
Tchange(k<<1,l,mid,x,c);
else
Tchange(k<<1|1,mid+1,r,x,c);
if (t[k<<1]<=t[k<<1|1])
t[k]=t[k<<1],id[k]=id[k<<1];
else
t[k]=t[k<<1|1],id[k]=id[k<<1|1];
}
pair<ll,int> Tquery(int k,int l,int r,int st,int en){
if (st<=l && r<=en)
return {t[k],id[k]};
int mid=l+r>>1;
pair<ll,int> res={INF,-1};
if (st<=mid)
res=Tquery(k<<1,l,mid,st,en);
if (mid<en)
res=min(res,Tquery(k<<1|1,mid+1,r,st,en));
return res;
}
void init(int _n){n=_n;Tbuild(1,0,n);}
void change(int x,ll c){Tchange(1,0,n,x,c);}
pair<ll,int> query(int l,int r){return l>r?make_pair(INF,-1):Tquery(1,0,n,l,r);}
} seg[2];
ll f[N][2];
int pre[N][2];
ll ps[N][2];
struct Info{int id,v;} sl[2][N],sr[2][N];
int tl[2],tr[2];
bool operator<(Info x,Info y){return x.v>y.v;}
int tmp[N];
void dfs(int i,int j,int *p,int &p0,int &p1){
if (i==0)
return;
if (j==0){
dfs(pre[i][j],j^1,p,p0,p1);
for (int k=pre[i][j]+1;k<=i;++k)
tmp[++p0]=p[k];
}
else{
for (int k=i;k>pre[i][j];--k)
tmp[--p1]=p[k];
dfs(pre[i][j],j^1,p,p0,p1);
}
}
void divide(int lv,int rv,int *p,int n){
if (n==0)
return;
if (lv==rv){
for (int i=1;i<=n;++i)
b[p[i]]=lv;
return;
}
int mid=lv+rv>>1;
for (int i=1;i<=n;++i){
L[i][0]=lower_bound(p+1,p+n+1,oL[p[i]][0])-p;
R[i][0]=upper_bound(p+1,p+n+1,oR[p[i]][0])-p-1;
L[i][1]=lower_bound(p+1,p+n+1,oL[p[i]][1])-p;
R[i][1]=upper_bound(p+1,p+n+1,oR[p[i]][1])-p-1;
}
for (int i=1;i<=n;++i){
ps[i][0]=ps[i-1][0]+abs(a[p[i]]-mid);
ps[i][1]=ps[i-1][1]+abs(a[p[i]]-(mid+1));
}
f[0][0]=f[0][1]=0;
tl[0]=tl[1]=tr[0]=tr[1]=1;
sl[0][1]=sl[1][1]={0,1};
sr[0][1]=sr[1][1]={0,0};
seg[0].init(n),seg[0].change(0,f[0][1]/*-ps[0][0]*/);
seg[1].init(n),seg[1].change(0,f[0][0]/*-ps[0][1]*/);
for (int i=1;i<=n;++i){
for (int j=0;j<2;++j){
while (tl[j] && sl[j][tl[j]].v>L[i][j]){
seg[j].change(sl[j][tl[j]].id,INF);
--tl[j];
}
int lst=i;
while (tr[j] && sr[j][tr[j]].v<=R[i][j]){
lst=sr[j][tr[j]].id;
--tr[j];
}
sr[j][++tr[j]]={lst,R[i][j]};
auto pos=lower_bound(sr[j]+1,sr[j]+tr[j]+1,(Info){0,i});
if (pos==sr[j]+tr[j]+1)
f[i][j]=INF,pre[i][j]=-1;
else{
pair<ll,int> tmp=seg[j].query(pos->id,i-1);
f[i][j]=tmp.first+ps[i][j];
pre[i][j]=tmp.second;
}
}
for (int j=0;j<2;++j){
if (pre[i][j^1]!=-1){
sl[j][++tl[j]]={i,i+1};
seg[j].change(i,f[i][j^1]-ps[i][j]);
}
sr[j][++tr[j]]={i,0};
}
}
int p0=0,p1=n+1;
dfs(n,f[n][0]<f[n][1]?0:1,p,p0,p1);
memcpy(p+1,tmp+1,sizeof(int)*n);
divide(lv,mid,p,p0);
divide(mid+1,rv,p+p0,n-p0);
}
int main(){
freopen("sailing.in","r",stdin);
freopen("sailing.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]),getmax(mx,a[i]);
for (int i=1;i<=n;++i)
oL[i][0]=oR[i][0]=oL[i][1]=oR[i][1]=i;
for (int i=1;i<=m;++i){
int op,l,r,k;
scanf("%d%d%d%d",&op,&l,&r,&k);
getmin(oL[k][op^1],l);
getmax(oR[k][op^1],r);
}
for (int i=1;i<=n;++i)
p[i]=i;
divide(1,mx,p,n);
ll ans=0;
for (int i=1;i<=n;++i)
ans+=abs(a[i]-b[i]);
printf("%lld\n",ans);
return 0;
}