题目

传送门

解法

鉴于网络流打代码从不管时间复杂度的成功经验,我上来就勇了一个 \(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;
} 
posted on 2021-02-25 15:47  Oxide  阅读(54)  评论(0编辑  收藏  举报