ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【洛谷P5331】通信

2021-08-27 20:00:23  阅读:173  来源: 互联网

标签:P5331 head 哨站 洛谷 int ll 通信 cost dis


题目

题目链接:https://www.luogu.com.cn/problem/P5331
\(n\) 个排成一列的哨站要进行通信。第 \(i\) 个哨站的频段为 \(a_i\)。
每个哨站 \(i\) 需要选择以下二者之一:

  1. 直接连接到控制中心,代价为 \(W\);
  2. 连接到前面的某个哨站 \(j\) (\(j<i\)),代价为 \(|a_i-a_j|\)。

每个哨站只能被后面的至多一个哨站连接。
请你求出最小可能的代价和。
\(n\leq 1000\),时限 \(5\rm s\)。

思路

考虑费用流。把每一个点 \(i\) 拆成两个点 \(i\) 和 \(i'\),从源点连向所有 \(i\),流量为 \(1\),费用为 \(0\);\(i\) 连向汇点,流量为 \(1\),费用为 \(W\),表示点 \(i\) 是某一条链的结尾。
然后对于所有点 \(i\) 连向 \(j'(j<i)\),流量为 \(1\),费用为 \(|a_i-a_j|\);\(i'\) 连向汇点,流量为 \(1\),费用为 \(0\)。
这样由于边数是 \(O(n^2)\) 的,跑不过去。考虑优化边数。
由于所有 \(i\) 连向 \(j'\) 都是从大的连向小的,考虑 CDQ 分治,对于当前区间 \([l,r]\),连上 \((mid,r]\) 流向 \([l,mid]\) 的边。
这个绝对值的代价,可以把 \([l,r]\) 内所有点的权值排序后去重,然后所有权值都建一个新的点,相邻权值对应的点之间连上流量为 \(+\infty\),费用为权值差的边。然后对于 \([l,mid]\) 的点,从这条链上对应权值的点连到 \(j'\),流量 \(1\) 费用 \(0\);对于 \((mid,r]\) 的点,连向这条链上对应的点,流量 \(1\) 费用 \(0\)。这样如果从 \(i\) 流向 \(j'\),就会先走到链上对应权值的点,然后这两个点之间的边费用之和恰好就是 \(|a_i-a_j|\)。
这样网络图的点数和边数都是 \(O(n\log n)\) 的了。跑费用流即可。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=1000010;
const ll Inf=1e18;
int n,n1,w,S,T,tot=1,a[N],b[N],head[N],pre[N];
ll ans,dis[N];
bool vis[N];

struct edge
{
	int next,to;
	ll flow,cost;
}e[N];

void add(int from,int to,ll flow,ll cost)
{
	e[++tot]=(edge){head[from],to,flow,cost};
	head[from]=tot;
	swap(from,to);
	e[++tot]=(edge){head[from],to,0,-cost};
	head[from]=tot;
}

void cdq(int l,int r)
{
	if (l==r) return;
	int mid=(l+r)>>1,len=r-l+1;
	for (int i=l;i<=r;i++) b[i-l+1]=a[i];
	sort(b+1,b+1+len);
	int cnt=unique(b+1,b+1+len)-b-1;
	for (int i=1;i<cnt;i++)
	{
		add(n1+i,n1+i+1,Inf,b[i+1]-b[i]);
		add(n1+i+1,n1+i,Inf,b[i+1]-b[i]);
	}
	for (int i=l;i<=mid;i++)
	{
		int p=lower_bound(b+1,b+1+cnt,a[i])-b+n1;
		add(p,i+n,1,0);
	}
	for (int i=mid+1;i<=r;i++)
	{
		int p=lower_bound(b+1,b+1+cnt,a[i])-b+n1;
		add(i,p,1,0);
	}
	n1+=cnt;
	cdq(l,mid); cdq(mid+1,r);
}

bool spfa()
{
	memset(dis,0x3f3f3f3f,sizeof(dis));
	deque<int> q;
	q.push_back(S); dis[S]=0;
	while (q.size())
	{
		int u=q.front(); q.pop_front();
		vis[u]=0;
		for (int i=head[u];~i;i=e[i].next)
		{
			int v=e[i].to;
			if (e[i].flow && dis[v]>dis[u]+e[i].cost)
			{
				dis[v]=dis[u]+e[i].cost; pre[v]=i;
				if (!vis[v])
				{
					vis[v]=1;
					if (q.size() && dis[v]<dis[q.front()]) q.push_front(v);
						else q.push_back(v);
				}
			}
		}
	}
	return dis[T]<Inf;
}

void addflow()
{
	ll minf=Inf;
	for (int i=T;i!=S;i=e[pre[i]^1].to)
		minf=min(minf,e[pre[i]].flow);
	for (int i=T;i!=S;i=e[pre[i]^1].to)
		e[pre[i]].flow-=minf,e[pre[i]^1].flow+=minf;
	ans+=minf*dis[T];
}

void mcmf()
{
	while (spfa()) addflow();
}

int main()
{
	memset(head,-1,sizeof(head));
	S=N-1; T=N-2;
	scanf("%d%d",&n,&w);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		add(S,i,1,0); add(i,T,1,w); add(i+n,T,1,0);
	}
	n1=2*n;
	cdq(1,n);
	mcmf();
	cout<<ans;
	return 0;
}

标签:P5331,head,哨站,洛谷,int,ll,通信,cost,dis
来源: https://www.cnblogs.com/stoorz/p/15195105.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有