题面
SOL
首先确定用费用流求解。
对于每一个点的贡献,只和向谁连边有关,和后面的点无关。
所以把每个点拆成 入点,出点。
1.将S和入点连边,
c
a
p
=
1
,
w
=
0
cap=1,w=0
cap=1,w=0
2.入点和T连一边,
c
a
p
=
1
,
w
=
W
cap=1,w=W
cap=1,w=W
3.出点和T连边:
c
a
p
=
1
,
w
=
0
cap=1,w=0
cap=1,w=0
4.入点和前
i
−
1
i-1
i−1个点的出点连边,
c
a
p
=
1
,
w
=
∣
a
[
j
]
−
a
[
i
]
∣
cap=1,w=|a[j]-a[i]|
cap=1,w=∣a[j]−a[i]∣。
但是第4种有 n 2 n^2 n2条边,费用流跑不过。。
优化建图: 如果不考虑前后,显然可以构造一条权值递增的链,每一个点向链连边,链向每一个点连边。链间费用为两端点的权值之差,链间连双向边。这样点与点之间的路径可以表示成 : i 的 入 点 流 向 链 中 a [ i ] , 在 链 上 流 向 a [ j ] , 链 上 a [ j ] 流 向 j 的 出 点 。 i的入点流向链中a[i],在链上流向a[j],链上a[j]流向j的出点。 i的入点流向链中a[i],在链上流向a[j],链上a[j]流向j的出点。
如何加上下标维度的限制? c d q 分 治 cdq分治 cdq分治。 构造 l o g n logn logn条链,处理区间 m i d mid mid以左和以右的进出。
CODE
#include<bits/stdc++.h>
using namespace std;
#define sf scanf
#define pf printf
#define ll long long
#define cs const
#define db double
#define ri register int
#define gc getchar()
#define in red()
inline int red(){
int num=0,f=1;char c=gc;
for(;!isdigit(c);c=gc)if(c=='-')f=-1;
for(;isdigit(c);c=gc)num=(num<<1)+(num<<3)+(c^48);
return num*f;
}
cs int N=1e5+10,M=3e6+10,inf=1e9;
cs ll INF=1e18;
int sum,a[N],tmp[N],n,W,S,T;
namespace FLOW{
int head[N],c[M],w[M],nxt[M],to[M],cnt=1,vis[N];
ll dis[M],ans;
inline void adde(int u,int v,int cap,int vl){
nxt[++cnt]=head[u];head[u]=cnt;to[cnt]=v;w[cnt]=vl;c[cnt]=cap;
nxt[++cnt]=head[v];head[v]=cnt;to[cnt]=u;w[cnt]=-vl;c[cnt]=0;
}
inline bool spfa(){
memset(vis,0,sizeof (int)*(sum+5));fill(dis+1,dis+sum+1,INF);
queue<int>q;
q.push(S);dis[S]=0;
while(!q.empty()){
int u=q.front();q.pop();
vis[u]=0;
for(ri i=head[u];i;i=nxt[i]){
int v=to[i];
if(c[i]&&dis[v]>dis[u]+w[i]){
dis[v]=dis[u]+w[i];
if(!vis[v])q.push(v),vis[v]=1;
}
}
}
return dis[T]^INF;
}
inline int dfs(int u,int tot){
if(u==T||!tot)return tot;
vis[u]=1;
int res=0,now;
for(ri i=head[u];i;i=nxt[i]){
int v=to[i];
if(!vis[v]&&c[i]&&dis[v]==dis[u]+w[i]){
now=dfs(v,min(c[i],tot-res));
if(now)c[i]-=now,c[i^1]+=now,res+=now,ans+=now*w[i];
}
if(res==tot)break;
}
if(!res)dis[u]=INF;
return vis[u]=0,res;
}
inline void run(){while(spfa()){memset(vis,0,sizeof (int)*(sum+5));dfs(S,inf);}}
}
using FLOW::adde;
inline void cdq(int l,int r){
if(l==r)return;
int mid=(l+r)>>1,top=0;
cdq(l,mid);cdq(mid+1,r);
for(ri i=l;i<=r;++i)tmp[++top]=a[i];
sort(tmp+1,tmp+top+1);top=unique(tmp+1,tmp+top+1)-tmp-1;
for(ri i=1;i<top;++i)adde(sum+i+1,sum+i,inf,tmp[i+1]-tmp[i]),adde(sum+i,sum+i+1,inf,tmp[i+1]-tmp[i]);
for(ri i=l;i<=r;++i){
int k=lower_bound(tmp+1,tmp+top+1,a[i])-tmp;
if(i<=mid)adde(sum+k,n+i,1,0);
else adde(i,sum+k,1,0);
}
sum+=top;
}
signed main(){
n=in;W=in;
S=(n<<1)+1;T=(n<<1)+2;sum=T;
for(ri i=1;i<=n;++i)a[i]=in,adde(S,i,1,0),adde(i,T,1,W),adde(n+i,T,1,0);
cdq(1,n);FLOW::run();
cout<<FLOW::ans<<'\n';
return 0;
}