P5331 [SNOI2019]通信

链接:https://www.luogu.com.cn/problem/P5331

题解:发现dp不好设状态,考虑网络流:

(s,i),流量1,费用0

(i,t),流量1,费用0

(i,t),流量1,费用0

(i,j)(i<j),流量1,费用|aibj|W

然后加上n×W

直接做不行,考虑优化:

这个堆模拟费用流要KDTO(n2nlogn)过不了。

介绍了一个dinic优化费用流的做法,但O(n34(n+m))也卡不过。

这时考虑数据结构优化连边,将序列cdq分治以下,排序后可以前缀优化连边,这样就可以通过了。

#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
struct node
{
  long long v,data,cost,nxt;
};
node edge[2000001];
int head[120003],len;
bool used[120003];
long long n,W,mincostmaxflow,a[1001],dis[120003],incf[120003],pv[120003],N,s,t;
int read()
{
  char c=0;
  int sum=0;
  while (c<'0'||c>'9')
    c=getchar();
  while ('0'<=c&&c<='9')
    {
      sum=sum*10+c-'0';
      c=getchar();
    }
  return sum;
}
void add(int x,int y,int z,int w)
{
  edge[++len].v=y,edge[len].data=z,edge[len].cost=w,edge[len].nxt=head[x],head[x]=len;
  edge[++len].v=x,edge[len].data=0,edge[len].cost=-w,edge[len].nxt=head[y],head[y]=len;
  return;
}
queue<int>q;
bool spfa()
{
  int top;
  for (int i=s;i<=t;++i)
    dis[i]=1e18;
  dis[s]=0;
  q.push(s);
  incf[s]=1e18;
  while (!q.empty())
    {
      top=q.front();
      q.pop();
      used[top]=0;
      for (int i=head[top];i>0;i=edge[i].nxt)
	if (edge[i].data&&dis[edge[i].v]>dis[top]+edge[i].cost)
	  {
	    incf[edge[i].v]=min(incf[top],edge[i].data);
	    dis[edge[i].v]=dis[top]+edge[i].cost;
	    pv[edge[i].v]=i;
	    if (!used[edge[i].v])
	      {
		used[edge[i].v]=1;
		q.push(edge[i].v);
	      }
	  }
    }
  if (dis[t]!=1e18)
      return 1;
  return 0;
}
long long EK()
{
  int x=t;
  while (x!=s)
    {
      edge[pv[x]].data-=incf[t];
      edge[pv[x]^1].data+=incf[t];
      x=edge[pv[x]^1].v;
    }
  return dis[t]*incf[t];
}
struct ps
{
  int num,data;
  bool operator < (const ps &a)const
  {
    return data<a.data;
  }
};
ps tong[100001];
void cdq(int l,int r)
{
  if (l==r)
    return;
  int mid=(l+r)/2;
  cdq(l,mid);
  sort(tong+l,tong+mid+1);
  cdq(mid+1,r);
  sort(tong+mid+1,tong+r+1);
  int pos=l-1;
  for (int i=mid+1;i<=r;++i)
    {
      while (pos<mid&&tong[pos+1].data<=tong[i].data)
	{
	  ++pos;
	  if (pos==l)
	      add(tong[l].num,++N,1,0);
	  else
	    {
	      add(tong[pos].num,++N,1,0);
	      add(N-1,N,1e9,tong[pos].data-tong[pos-1].data);
	    }
	}
      if (pos>=l)
	  add(N,tong[i].num+n,1,tong[i].data-tong[pos].data-W);
    }
  pos=mid+1;
  for (int i=r;i>=mid+1;--i)
    {
      while (pos>l&&tong[pos-1].data>tong[i].data)
        {
          --pos;
          if (pos==mid)
              add(tong[mid].num,++N,1,0);
          else
            {
              add(tong[pos].num,++N,1,0);
              add(N-1,N,1e9,tong[pos+1].data-tong[pos].data);
            }
	}
      if (pos<=mid)
	  add(N,tong[i].num+n,1,tong[pos].data-tong[i].data-W);
    }
  return;
}
int main()
{
  len=1;
  n=read(),W=read();
  for (int i=1;i<=n;++i)
    {
      a[i]=read();
      tong[i].num=i,tong[i].data=a[i];
    }
  N=2*n;
  s=0;
  for (int i=1;i<=n;++i)
      add(s,i,1,0);
  cdq(1,n);
  t=N+1;
  for (int i=1;i<=n;++i)
    {
      add(i+n,t,1,0);
      add(i,t,1,0);
    }
  while (spfa())
      mincostmaxflow+=EK();
  printf("%lld\n",mincostmaxflow+n*W);
  return 0;
}
posted @   zhouhuanyi  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
主题色彩