「SNOI2019」通信

「SNOI2019」通信

传送门

Loj

题解

首先考虑一个最暴力的\(O(n^2)\)连边的费用流,即拆点然后\(s\rightarrow i,i+n \rightarrow t,s\rightarrow i+n\).

中间的连边考虑\(i \rightarrow j+n\)表示\(j\)连到了\(i\)这个点.

左右的边数都是\(O(n)\)级别的,考虑中间连边的优化.

可以想想\(cdq\)分治,考虑左边的点对右边的连边,这个东西可以用前后缀优化连边,然后建一排虚点即可.

代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<iostream>
#include<set>
#include<map>
using namespace std;
#define mp make_pair
#define ll long long
#define re register
typedef pair<int,int> pii;
#define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
inline int gi()
{
	int f=1,sum=0;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return f*sum;
}
const int N=200010,M=2000010,Inf=1e9+10;
int front[N],cnt,n,m,vis[N],s,t,fa[N],from[N],W,a[N],p[N],tot,p1[N];
struct node{int to,nxt,w,f;}e[M<<1];
ll ans,dis[N];
queue<int>Q;
void Add(int u,int v,int w,int f)
{
	e[cnt]=(node){v,front[u],w,f};front[u]=cnt++;
	e[cnt]=(node){u,front[v],0,-f};front[v]=cnt++;
}
bool SPFA()
{
	memset(dis,63,sizeof(dis));memset(vis,0,sizeof(vis));
	Q.push(s);dis[s]=0;vis[s]=1;
	while(!Q.empty())
	{
		int u=Q.front();Q.pop();vis[u]=0;
		for(int i=front[u];~i;i=e[i].nxt)
		{
			int v=e[i].to;
			if(e[i].w&&dis[v]>dis[u]+e[i].f)
			{
				dis[v]=dis[u]+e[i].f;fa[v]=u;from[v]=i;
				if(!vis[v]){Q.push(v);vis[v]=1;}
			}
		}
	}
	if(dis[t]>1e9+10)return false;
	int di=Inf;
	for(int i=t;i!=s;i=fa[i])di=min(di,e[from[i]].w);
	ans+=1ll*di*dis[t];
	for(int i=t;i!=s;i=fa[i])e[from[i]].w-=di,e[from[i]^1].w+=di;
	return true;
}
void solve(int l,int r)
{
	if(l==r)return;
	int mid=(l+r)>>1,sz=0;
	solve(l,mid);solve(mid+1,r);
	for(int i=l,j=mid+1;i<=mid||j<=r;)
		p1[++sz]=p[j>r||(i<=mid&&a[p[i]]<a[p[j]])?i++:j++];
	for(int i=1;i<sz;i++)
	{
		Add(tot+i,tot+i+1,Inf,a[p1[i+1]]-a[p1[i]]);
		Add(tot+i+1,tot+i,Inf,a[p1[i+1]]-a[p1[i]]);
	}
	for(int i=1;i<=sz;i++)
		if(p1[i]<=mid)Add(p1[i],tot+i,1,0);
		else Add(tot+i,p1[i]+n,1,0);
	tot+=sz;
	for(int i=l;i<=r;i++)p[i]=p1[i-l+1];
}
int main()
{
	memset(front,-1,sizeof(front));
	n=gi();W=gi();s=0;tot=t=n<<1|1;
	for(int i=1;i<=n;i++)a[i]=gi(),p[i]=i;
	for(int i=1;i<=n;i++)Add(s,i,1,0),Add(i+n,t,1,0),Add(s,i+n,1,W);
	solve(1,n);
	while(SPFA());
	printf("%lld\n",ans);
	return 0;
}
posted @ 2020-06-01 17:34  fexuile  阅读(112)  评论(0编辑  收藏  举报