zoukankan      html  css  js  c++  java
  • 「SNOI2019」通信

    题目

    还好我没生在陕西啊

    首先发现这个题不能(dp),数据范围不大,好像一种网络流的样子啊

    哎等等,这样向后面连边不是一个(DAG)吗,这不是最小权路径覆盖的板子吗

    于是我们套路的拆点,对于一个点(i)我们拆成(i)(i'),源点向点(i)连费用为(0)容量为(1)的边,(i')向汇点连费用为(0)容量为(1)的边

    之后我们把让(S)(i')直接连费用为(W)容量为(1)的边,表示直接连到控制中心

    对于点(i),我们向(j'(j>i))连容量为(1)费用为(|a_i-a_j|)的边,表示点(j)接到了点(i)后面

    于是现在我们一个最小费用最大流就好了

    之后喜提(TLE)

    我们发现我们好像建出了(n^2)级别的边

    我们考虑我们连边的方式,这样向后面一个区间连边的方式让人好熟悉啊,这不是线段树优化建图吗

    但是我们注意到这里的边的费用好像不是一样的,但是这个绝对值给了我们分类讨论的可能

    对于一个点(i),我们建出一个虚点(x')(i)(x')连费用为(a_i)的边,让这个虚点(x')(j(j>i,a_j<a_i))连费用为(-a_j)的边,(a_j>a_i)同理,这样我们就能实现绝对值这个问题了

    那么我们如何快速向一个点之后的所有大于它或小于它的点连边呢

    直接主席树优化建图就好了,这样我们的点数和边数都是(nlogn)级别的了

    但是还是(T),发现我们建了两万多个点和十万多条边

    写一个快一点的费用流就好了,用zkw费用流和(slf)优化的spfa勉勉强强能跑过的样子

    另外(loj)上的大爷们写的看起来好像(cdq)分治优化建图的洞西看起来好神仙啊

    代码

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define re register
    #define LL long long
    #pragma GCC optimize(3)
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    inline int read() {
    	char c=getchar();int x=0;while(c<'0'||x>'9') c=getchar();
    	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
    }
    const LL inf=999999999999;
    const int fnf=999999999;
    const int maxn=2e3+5;
    const int M=4e5+6;
    int head[M],vis[M],a[maxn],c[maxn],b[maxn];LL d[M];
    int rt[maxn][2],l[M],r[M];
    struct E{int v,nxt,w,f;}e[200005];
    int n,num=1,S,T,W,sz,cnt;
    inline void C(int x,int y,int w,int f) {
    	e[++num].v=y;e[num].nxt=head[x];
    	e[num].w=w;e[num].f=f;head[x]=num;
    }
    std::deque<int> q;
    inline void add(int x,int y,int w,int f) {C(x,y,w,f),C(y,x,-1*w,0);}
    inline int SPFA() {
    	for(re int i=S;i<=cnt;i++) d[i]=inf,vis[i]=0;
    	d[T]=0,q.push_back(T);
    	while(!q.empty()) {
    		int k=q.front();q.pop_front();vis[k]=0;
    		for(re int i=head[k];i;i=e[i].nxt)
    		if(e[i^1].f&&d[e[i].v]>d[k]+e[i^1].w) {
    			d[e[i].v]=d[k]+e[i^1].w;
    			if(!vis[e[i].v]) {
    				vis[e[i].v]=1;
    				if(q.empty()) {q.push_back(e[i].v);continue;}
    				if(d[e[i].v]<d[q.front()]) q.push_front(e[i].v);
    					else q.push_back(e[i].v);
    			}
    		}
    	}
    	return d[S]<inf;
    }
    int dfs(int x,int now) {
    	if(x==T||!now) return now;
    	int flow=0,ff;vis[x]=1;
    	for(re int i=head[x];i;i=e[i].nxt)
    	if(e[i].f&&!vis[e[i].v]&&d[e[i].v]==d[x]+e[i^1].w) {
    		ff=dfs(e[i].v,min(e[i].f,now));
    		if(ff<=0) continue;
    		flow+=ff,now-=ff,e[i].f-=ff,e[i^1].f+=ff;
    		if(!now) break;
    	}
    	return flow;
    }
    inline int find(int x) {
    	int lx=1,ry=sz;
    	while(lx<=ry) {
    		int mid=lx+ry>>1;
    		if(c[mid]==x) return mid;
    		if(c[mid]<x) lx=mid+1;
    			else ry=mid-1;
    	}
    	return 0;
    }
    int ins(int pre,int x,int y,int pos,int a,int b) {
    	int root=++cnt;
    	if(x==y) {
    		add(root,a,b*c[x],1);
    		return root;
    	}
    	int mid=x+y>>1;
    	l[root]=l[pre],r[root]=r[pre];
    	if(pos<=mid) l[root]=ins(l[pre],x,mid,pos,a,b);
    		else r[root]=ins(r[pre],mid+1,y,pos,a,b);
    	if(l[root]) add(root,l[root],0,fnf);
    	if(r[root]) add(root,r[root],0,fnf);
    	return root;
    }
    void Con(int g,int now,int x,int y,int lx,int ry) {
    	if(!now) return;
    	if(lx<=x&&ry>=y) {add(g,now,0,fnf);return;} 
    	int mid=x+y>>1;
    	if(lx<=mid) Con(g,l[now],x,mid,lx,ry);
    	if(ry>mid) Con(g,r[now],mid+1,y,lx,ry);
    }
    int main() {
    	n=read(),W=read();S=0,T=n+n+1;cnt=T;
    	for(re int i=1;i<=n;i++) a[i]=read();
    	for(re int i=1;i<=n;i++) add(S,i+n,W,1);
    	for(re int i=1;i<=n;i++) add(S,i,0,1);
    	for(re int i=1;i<=n;i++) add(i+n,T,0,1);
    	for(re int i=1;i<=n;i++) c[i]=a[i];
    	std::sort(c+1,c+n+1);sz=std::unique(c+1,c+n+1)-c-1;
    	for(re int i=1;i<=n;i++) b[i]=a[i],a[i]=find(a[i]);
    	for(re int i=n;i>1;--i) 
    		rt[i][0]=ins(rt[i+1][0],1,sz,a[i],i+n,-1),
    		rt[i][1]=ins(rt[i+1][1],1,sz,a[i],i+n,1);
    	for(re int i=1;i<n;i++) {
    		++cnt;
    		add(i,cnt,b[i],1);
    		Con(cnt,rt[i+1][0],1,sz,1,a[i]);
    		++cnt;
    		add(i,cnt,-1*b[i],1);
    		Con(cnt,rt[i+1][1],1,sz,a[i],sz);
    	} 
    	LL ans=0;
    	while(SPFA()) {
    		vis[T]=1;
    		while(vis[T]) {
    			for(re int i=S;i<=cnt;i++) vis[i]=0;
    			ans+=1ll*dfs(S,fnf)*d[S];
    		}
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    hihoCoder #1176 : 欧拉路·一 (简单)
    228 Summary Ranges 汇总区间
    227 Basic Calculator II 基本计算器II
    226 Invert Binary Tree 翻转二叉树
    225 Implement Stack using Queues 队列实现栈
    224 Basic Calculator 基本计算器
    223 Rectangle Area 矩形面积
    222 Count Complete Tree Nodes 完全二叉树的节点个数
    221 Maximal Square 最大正方形
    220 Contains Duplicate III 存在重复 III
  • 原文地址:https://www.cnblogs.com/asuldb/p/10786155.html
Copyright © 2011-2022 走看看