zoukankan      html  css  js  c++  java
  • 【洛谷5331】[SNOI2019] 通信(CDQ分治+费用流)

    点此看题面

    • (n)个哨站,第(i)个哨站段频为(a_i)
    • (i)个哨站可以选择花费(m)的代价直接与控制中心相连,也可以选择花费(|a_i-a_j|)的代价与第(j)个哨站((j<i))相连。每个哨站最多与一个后面的哨站相连。
    • 求最小代价。
    • (nle10^3)

    暴力费用流

    把每个点拆成两个,分别表示向前连和向后连。

    具体建图如下:

    • 从超级源向(i)连容量为(1)、费用为(0)的边。(表示向前连的点)
    • (n+i)向超级汇连容量为(1)、费用为(0)的边。(表示向后连的点)
    • (i)向超级汇连容量为(1)、费用为(m)的边。(直接与控制中心相连)
    • (i)(n+j)(j<i))连容量为(1)、费用为(|a_i-a_j|)的边。(让(i)(j)配对)

    当然直接这么做边数(O(n^2)),肯定过不去。

    (CDQ)分治优化建图

    核心是要优化(i)(n+j)连的费用为(|a_i-a_j|)的边。

    假设当前处理到区间([l,r]),则我们需要从右区间([mid+1,r])向左区间([l,mid])连边。

    分别将两个区间内的元素排序,对左区间建一排虚点辅助连边,相邻辅助点间所连边的代价为对应元素的差值。

    然后我们枚举右区间的每个点,双指针维护出它的前驱和后继,向这两个辅助点连边,那么只要沿着辅助点之间的边走就能通过差值的累积表示出所有需要连的边了。

    代码:(O( exttt{Dinic}))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Rg register
    #define RI Rg int
    #define Cn const
    #define CI Cn int&
    #define I inline
    #define W while
    #define N 1000
    #define LL long long
    #define inf (int)1e9
    #define INF (LL)1e18
    using namespace std;
    int n,m,ct,a[N+5];
    namespace D//最小费用最大流
    {
    	#define s (2*n+1)
    	#define t (2*n+2)
    	#define PS (N*15)
    	#define ES (PS*5)
    	#define add(x,y,f,c) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].F=f,e[ee].C=c)
    	int ee=1,lnk[PS+5];struct edge {int to,nxt,F,C;}e[2*ES+5];
    	I void Add(CI x,CI y,CI f,CI c) {add(x,y,f,c),add(y,x,0,-c);}
    	int p[PS+5],IQ[PS+5];LL C[PS+5];queue<int> q;I bool SPFA()
    	{
    		RI i;for(i=1;i<=ct;++i) C[i]=INF;C[s]=0,q.push(s),IQ[s]=1;
    		RI k;W(!q.empty()) for(IQ[k=q.front()]=0,q.pop(),i=lnk[k];i;i=e[i].nxt) e[i].F&&
    			C[k]+e[i].C<C[e[i].to]&&(C[e[i].to]=C[k]+e[p[e[i].to]=i].C,!IQ[e[i].to]&&(q.push(e[i].to),IQ[e[i].to]=1));
    		return C[t]^INF;
    	}
    	I void MCMF() {RI x;LL g=0;W(SPFA()) {g+=C[x=t];W(x^s) --e[p[x]].F,++e[p[x]^1].F,x=e[p[x]^1].to;}printf("%lld
    ",g);}
    }
    I bool cmp(CI x,CI y) {return a[x]<a[y];}
    int id[N+5];I void CDQ(CI l,CI r)//CDQ分治优化建图
    {
    	#define P(i) (ct+(i)-l+1)
    	if(l==r) return;RI i,j,mid=l+r>>1;for(i=l;i<=r;++i) id[i]=i;sort(id+l,id+mid+1,cmp),sort(id+mid+1,id+r+1,cmp);//分别排序两个子区间
    	for(i=l;i<=mid;++i) D::Add(P(i),n+id[i],1,0),i^l&&//对左区间建一排辅助点
    		(D::Add(P(i-1),P(i),inf,a[id[i]]-a[id[i-1]]),D::Add(P(i),P(i-1),inf,a[id[i]]-a[id[i-1]]),0);//相邻辅助点间所连边的代价为对应元素的差值
    	for(i=mid+1,j=l;i<=r;++i) {W(j<=mid&&a[id[i]]>a[id[j]]) ++j;//双指针维护右区间每个点的前驱后继
    		j^l&&(D::Add(id[i],P(j-1),1,a[id[i]]-a[id[j-1]]),0),j<=mid&&(D::Add(id[i],P(j),1,a[id[j]]-a[id[i]]),0);}//向这两个辅助点连边
    	ct+=mid-l+1,CDQ(l,mid),CDQ(mid+1,r);//分治
    }
    int main()
    {
    	RI i;for(scanf("%d%d",&n,&m),i=1;i<=n;++i)
    		scanf("%d",a+i),D::Add(2*n+1,i,1,0),D::Add(i,2*n+2,1,m),D::Add(n+i,2*n+2,1,0);//拆成的两个点与超级源汇之间的连边
    	return ct=2*n+2,CDQ(1,n),D::MCMF(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    ARM Linux异常处理之data abort(二)【转】
    ARM Linux异常处理之data abort(一)【转】
    arm的mmu学习【转】
    使用Squashfs和Overlayfs提高嵌入式Linux文件系统可靠性【转】
    【iView】100. iView踩坑集锦
    【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event 实现 监听者模式,实现补充业务逻辑开发
    【IDEA】idea打开新项目,左下角的工作栏中没有显示Services解决办法
    【java】ObjectOutputStream & ObjectInputStream 多次写入发生重复写入相同数据的问题
    【JS】时间工具类
    【MySQL】mysql优化集锦
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5331.html
Copyright © 2011-2022 走看看