zoukankan      html  css  js  c++  java
  • 【洛谷P5331】通信

    题目

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

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

    每个哨站只能被后面的至多一个哨站连接。
    请你求出最小可能的代价和。
    (nleq 1000),时限 (5 m 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(nlog 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;
    }
    
  • 相关阅读:
    python 回溯法 记录
    java提高篇(二三)-----HashMap
    java提高篇(二二)---LinkedList
    java提高篇(二一)-----ArrayList
    java提高篇(二十)-----集合大家族
    java提高篇(十九)-----数组之二
    java提高篇(十八)-----数组之一:认识JAVA数组
    java提高篇(十七)-----异常(二)
    java提高篇(十六)-----异常(一)
    jQuery读取和设定KindEditor的值
  • 原文地址:https://www.cnblogs.com/stoorz/p/15195105.html
Copyright © 2011-2022 走看看