「SNOI2019」通信
传送门
题解
首先考虑一个最暴力的(O(n^2))连边的费用流,即拆点然后(s ightarrow i,i+n ightarrow t,s ightarrow i+n).
中间的连边考虑(i ightarrow 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
",ans);
return 0;
}