题目
解法
鉴于网络流打代码从不管时间复杂度的成功经验,我上来就勇了一个 \(n^2\) 建边。
好!\(90\text{ pts}\),很有精神。
朴素建边就是对于哨站 \(i\),连接 \((S,i,1,W),(i,T,1,0)\)。考虑到 \((S,i,1,W)\) 的边权是固定的,我们建一个虚点 \(n+i\),连接 \((S,n+i,1,0)\),并将 \(j>i\) 连接 \((n+i,j,1,|a_i-a_j|)\)。
建边的主要花费就是必须将 \(i,j\) 一一匹配。其实可以不用这么精确。
我们可以进行分治,钦定某个点属于较小部分,某个点属于较大部分(这个用 \(mid\) 来划分)。对于每一层,将权值排个序,对权值建点,再在相邻权值间连接流量为 \(\text{infty}\),权值为相邻权值相减的边。如果这个点属于较小部分,则将这个点的虚点向它在当前层的权值点连边;如果这个点属于较大部分,则将它在当前层的权值点向这个点连边。这就达到了枚举 \(i,j\) 的目的。
代码
#include <cstdio>
#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
#define print(x,y) write(x),putchar(y)
template <class T> inline T read(const T sample) {
T x=0; int f=1; char s;
while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
return x*f;
}
template <class T> inline void write(const T x) {
if(x<0) return (void) (putchar('-'),write(-x));
if(x>9) write(x/10);
putchar(x%10^48);
}
template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
template <class T> inline T fab(const T x) {return x>0?x:-x;}
template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}
template <class T> inline T Swap(T &x,T &y) {x^=y^=x^=y;}
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=1005,inf=0x3f3f3f3f,maxm=1e5;
bool vis[maxn*20];
int n,W,a[maxn],S,T,idx;
ll MinCost,dis[maxn*20];
int arc[maxn*20];
int cnt=1,head[maxn*20],to[maxm],nxt[maxm],flow[maxm],Cost[maxm];
queue <int> q;
void addEdge(int u,int v,int w,int c) {
nxt[++cnt]=head[u],to[cnt]=v,flow[cnt]=w,Cost[cnt]=c,head[u]=cnt;
nxt[++cnt]=head[v],to[cnt]=u,flow[cnt]=0,Cost[cnt]=-c,head[v]=cnt;
}
bool SPFA() {
rep(i,0,idx) vis[i]=0,dis[i]=inf,arc[i]=head[i];
q.push(S),vis[S]=1,dis[S]=0;
while(!q.empty()) {
int u=q.front(); q.pop(); vis[u]=0;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
if(flow[i]>0 && dis[v]>dis[u]+Cost[i]) {
dis[v]=dis[u]+Cost[i];
if(!vis[v]) q.push(v),vis[v]=1;
}
}
}
return dis[T]^inf;
}
int dfs(int u,int CanFlow) {
vis[u]=1;
if(u==T) return CanFlow;
int SumFlow=0,d;
for(int i=arc[u];i;i=nxt[i]) {
int v=to[i]; arc[u]=i;
if((!vis[v] || v==T) && flow[i]>0 && dis[v]==dis[u]+Cost[i]) {
d=dfs(v,min(CanFlow,flow[i]));
if(!d) dis[v]=inf;
SumFlow+=d,CanFlow-=d;
flow[i]-=d,flow[i^1]+=d;
MinCost+=d*Cost[i];
if(!CanFlow) break;
}
}
return SumFlow;
}
void Dinic() {
while(SPFA()) {
vis[T]=1;
while(vis[T]) {
rep(i,0,T) vis[i]=0;
dfs(S,inf);
}
}
printf("%lld\n",MinCost);
}
void Link(int l,int r) {
if(l==r) return;
int mid=l+r>>1;
Link(l,mid),Link(mid+1,r);
int t[maxn],tot=0;
rep(i,l,r) t[++tot]=a[i];
sort(t+1,t+tot+1);
tot=unique(t+1,t+tot+1)-t-1;
rep(i,1,tot-1) addEdge(idx+i,idx+i+1,inf,t[i+1]-t[i]),addEdge(idx+i+1,idx+i,inf,t[i+1]-t[i]);
rep(i,l,r) {
int j=lower_bound(t+1,t+tot+1,a[i])-t;
if(i<=mid) addEdge(n+i,idx+j,1,0);
else addEdge(idx+j,i,1,0);
}
idx+=tot;
}
int main() {
n=read(9),W=read(9); T=idx=2*n+1;
rep(i,1,n) a[i]=read(9),addEdge(S,i,1,W),addEdge(i,T,1,0),addEdge(S,n+i,1,0);
Link(1,n),Dinic();
return 0;
}