传送门
n n n 个排成一列的哨站要进行通信。第 i i i 个哨站的频段为 a i a_i ai。
每个哨站 i i i 需要选择以下二者之一:
每个哨站只能被后面的至多一个哨站连接。
请你求出最小可能的代价和。
数据范围: 1 ≤ n ≤ 1000 1\le n \le 1000 1≤n≤1000, 0 ≤ W , a i ≤ 1 0 9 0\le W,a_i\le 10^9 0≤W,ai≤109。
应该比较容易看出是费用流吧。
但是,对于点 i i i,如果暴力对每个点 j j j( j < i jj<i)都建边,会有 O ( n 2 ) O(n^2) O(n2) 的边,会 T 掉。
仔细想一想,我们从 i i i 向 [ 1 , j − 1 ] [1,j-1] [1,j−1] 所有点连边,有一点线段树优化建图的意味。
可是,那个绝对值用普通线段树比较难搞,我们考虑权值线段树 / / /主席树。
由于权值较大,要离散化。设原来的权值为 b i b_i bi,离散过后的权值为 a i a_i ai,令 k = max i = 1 n { a i } k=\max\limits_{i=1}^n\{a_i\} k=i=1maxn{ai}(即上界)。
我们建两颗主席树,分别表示 b j < b i b_j
那么每次对 i i i 进行连边操作时,我们就向第一颗 [ 1 , a i ] [1,a_i] [1,ai] 连 b i b_i bi 的边,向第二颗 [ a i , k ] [a_i,k] [ai,k] 连 − b i -b_i −bi 的边。
不难发现这和原来的连边的等价的。
这样,边的数量就减到了 O ( n log n ) O(n\log n) O(nlogn),便能通过此题。
#include
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int N=2e5+5,M=3e6+5;
int n,W,k,t=1,S,T,rt1,rt2,tot;
int a[N],b[N],f[N],vis[N],walk[N];
int first[N],v[M],w[M],nxt[M],cost[M];
ll d[N],mincost;
int Abs(int x) {return x>0?x:-x;}
void add(int x,int y,int f,int c){
nxt[++t]=first[x],first[x]=t,v[t]=y,w[t]=f,cost[t]=c;
nxt[++t]=first[y],first[y]=t,v[t]=x,w[t]=0,cost[t]=-c;
}
queue<int>Q;
bool spfa(){
for(int i=S;i<=tot;++i){
f[i]=first[i],d[i]=1e18,vis[i]=walk[i]=0;
}
d[S]=0,Q.push(S);
while(!Q.empty()){
int x=Q.front();Q.pop();
vis[x]=0;
for(int i=first[x];i;i=nxt[i]){
int to=v[i];
if(w[i]&&d[to]>d[x]+cost[i]){
d[to]=d[x]+cost[i];
if(!vis[to]) Q.push(to),vis[to]=1;
}
}
}
return d[T]!=1e18;
}
int dinic(int now,int flow){
if(now==T){
mincost+=d[T]*flow;return flow;
}
int delta,ans=0;walk[now]=1;
for(int &i=f[now];i;i=nxt[i]){
int x=v[i];
if(!walk[x]&&w[i]&&d[x]==d[now]+cost[i]){
delta=dinic(x,min(flow,w[i]));
w[i]-=delta,w[i^1]+=delta,flow-=delta,ans+=delta;
if(!flow) return ans;
}
}
return ans;
}
void discrete(){
sort(b+1,b+n+1),k=unique(b+1,b+n+1)-(b+1);
for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+k+1,a[i])-b;
}
int lc[N],rc[N];
#define mid ((l+r)>>1)
void edge(int rt,int l,int r,int x,int y,int p,int val){
if(!rt) return;
if(l>=x&&r<=y) {add(p,rt,1,val);return;}
if(x<=mid) edge(lc[rt],l,mid,x,y,p,val);
if(y >mid) edge(rc[rt],mid+1,r,x,y,p,val);
}
void Insert(int rt,int &u,int l,int r,int pos,int p,int val){
u=++tot;
lc[u]=lc[rt],rc[u]=rc[rt];
if(l==r) {add(u,p,1,val);return;}
if(pos<=mid) Insert(lc[rt],lc[u],l,mid,pos,p,val);
else Insert(rc[rt],rc[u],mid+1,r,pos,p,val);
if(lc[u]) add(u,lc[u],1e9,0);
if(rc[u]) add(u,rc[u],1e9,0);
}
#undef mid
int main(){
scanf("%d%d",&n,&W);
S=0,T=2*n+2;
for(int i=1;i<=n;++i){
scanf("%d",&a[i]),b[i]=a[i];
add(S,i,1,0),add(n+i,T,1,0),add(i,2*n+1,1,W);
}
add(2*n+1,T,1e9,0),discrete(),tot=T;
for(int i=1;i<=n;++i){
edge(rt1,1,k,1,a[i],i,b[a[i]]);
edge(rt2,1,k,a[i],k,i,-b[a[i]]);
Insert(rt1,rt1,1,k,a[i],n+i,-b[a[i]]);
Insert(rt2,rt2,1,k,a[i],n+i,b[a[i]]);
}
while(spfa()) dinic(S,inf);
printf("%lld\n",mincost);
return 0;
}