zoukankan      html  css  js  c++  java
  • LOJ#3097 [SNOI2019]通信 最小费用最大流+cdq分治/主席树/分块优化建图

    瞎扯

    我们网络流模拟赛(其实是数据结构模拟赛)的T2。

    考场上写主席树写自闭了,直接交了(80pts)的暴力,考完出来突然发现:

    • woc这个题一个cdq几行就搞定了!

    题意简述

    (n)个哨站,第(i)个哨站的频段为(a_i)。每个哨站可以花费(W)连接中心,也可以花费(|a_j-a_i|)连接到第(j)个哨站((j<i))。

    每个哨站最多只能被连接一次,求所有哨站连接的最小花费。

    做法

    Luogu能过的暴力

    由最多只能被连接一次想到流量限制(显然),发现题目要求最小花费,所以建图跑最小费用最大流。

    考虑暴力建边,将每个点拆成(2)个点,一个表示直接连接中心,另一个限制流量。

    • 所以有(S xrightarrow{1/0} i xrightarrow{1/W} T)(i xrightarrow{infty/|a_i-a_j|} j')(i' xrightarrow{1/0} T)

    考场上我写完建图和zkw费用流就跑了,然后突然发现边是(n^2)的,跑极限数据要跑(100s+),但是我后面交luogu竟然过了???

    暴力代码

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define re register int
    #define db double
    #define in inline
    namespace fast_io
    {
    	char buf[1<<12],*p1=buf,*p2=buf,sr[1<<23],z[23],nc;int C=-1,Z=0;
    	in char gc() {return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<12,stdin),p1==p2)?EOF:*p1++;}
    	in ll read()
    	{
    		ll x=0,y=1;while(nc=gc(),(nc<48||nc>57)&&nc!=-1) if(nc==45) y=-1;
    		x=nc-48;while(nc=gc(),47<nc&&nc<58) x=(x<<3)+(x<<1)+(nc^48);return x*y;
    	}
    	in db gf() {re a=read(),b=(nc!='.')?0:read(),c=ceil(log10(b));return (b?a+(db)b/pow(10,c):a);}
    	in int gs(char *s) {char c,*t=s;while(c=gc(),c<32);*s++=c;while(c=gc(),c>32)*s++=c;return s-t;}
    	template <typename T>
    	in void write(T x,char t)
    	{
    		re y=0;if(x<0) y=1,x=-x;while(z[++Z]=x%10+48,x/=10);
    		if(y) z[++Z]='-';while(sr[++C]=z[Z],--Z);sr[++C]=t;
    	}
    	in void write(char *s) {re l=strlen(s);for(re i=0;i<l;i++,*s++)sr[++C]=*s;sr[++C]='
    ';}
    	in void ot() {fwrite(sr,1,C+1,stdout);C=-1;}
    };
    using namespace fast_io;
    const int N=2e3+5;
    const ll inf=1e18;
    int cnt=1,sum,tot,n,s,t,m,k;
    int h[N],l,r,q[N],vis[N],a[N];
    ll ans,maxflow,dis[N];
    struct did{int u,next,to,f,w;}e[N*N];
    in void add(re a,re b,re c,re d)
    {
    	e[++cnt]=(did){a,h[a],b,c,d},h[a]=cnt;
    	e[++cnt]=(did){b,h[b],a,0,-d},h[b]=cnt;
    }
    int spfa()
    {
    	memset(vis,0,sizeof(vis));
    	for(re i=s;i<=t;i++) dis[i]=i==s?0:inf;
    	queue<int>q;q.push(s);vis[s]=1;
    	while(!q.empty())
    	{
    		re i=q.front();vis[i]=0;q.pop();
    		for(re j=h[i],k;k=e[j].to,j;j=e[j].next)
    		if(e[j].f&&dis[k]>dis[i]+e[j].w)
    		{
    			dis[k]=dis[i]+e[j].w;
    			if(!vis[k]) q.push(k),vis[k]=1;
    		}
    	}	
    	return dis[t]<inf;
    }
    in int dfs(re u,re f)
    {
    	if(u==t) return f; vis[u]=1;
    	re res=0;
    	for(re i=h[u],v;v=e[i].to,i&&res<f;i=e[i].next)
    	if(e[i].f&&!vis[v]&&dis[v]==dis[u]+e[i].w)
    	{
    		re t=dfs(v,min(f-res,e[i].f));
    		res+=t;ans+=(ll)e[i].w*t;
    		e[i].f-=t;e[i^1].f+=t;
    	}
    	if(!res) dis[u]=inf;
    	return vis[u]=0,res;
    }
    in void zkw() {while(spfa()) memset(vis,0,sizeof(vis)),maxflow+=dfs(s,1e9);}
    int main()
    {
    	n=read();m=read();s=0,t=n*2+1;
    	for(re i=1;i<=n;i++) a[i]=read(),add(s,i,1,0),add(i,t,1,m),add(n+i,t,1,0);
    	for(re i=1;i<n;i++) for(re j=i+1;j<=n;j++) 
    	if(abs(a[i]-a[j])<m) add(j,n+i,1,abs(a[i]-a[j]));
    	zkw();write(ans,'
    ');
    	return ot(),0;
    }
    

    正解(暴力优化)

    发现本题瓶颈在于一个点向一个区间连边,而且有费用

    考场上没想到可以转化负数,一直不知道如何解决绝对值。这里采用常数和花费更为优秀的cdq分治,分治后将区间内所有的虚点间连接费用为(Delta a)的边,二分保证连边的(a_j)都大于(a_i)即可。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define re register int
    #define db double
    #define in inline
    namespace fast_io
    {
    	char buf[1<<12],*p1=buf,*p2=buf,sr[1<<23],z[23];int C=-1,Z=0;
    	in char gc() {return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<12,stdin),p1==p2)?EOF:*p1++;}
    	in ll read()
    	{
    		ll x=0,y=1;char c;while(c=gc(),(c<48||c>57)&&c!=-1) if(c==45) y=-1;
    		x=c-48;while(c=gc(),47<c&&c<58) x=(x<<3)+(x<<1)+(c^48);return x*y;
    	}
    	in db gf() {int a=read(),b=read(),c=ceil(log10(b));return (b?a+(db)b/pow(10,c):a);}
    	in int gs(char *s) {char c,*t=s;while(c=gc(),c<32);*s++=c;while(c=gc(),c>32)*s++=c;return s-t;}
    	template <typename T>
    	in void write(T x,char t)
    	{
    		re y=0;if(x<0) y=1,x=-x;while(z[++Z]=x%10+48,x/=10);
    		if(y) z[++Z]='-';while(sr[++C]=z[Z],--Z);sr[++C]=t;
    	}
    	in void write(char *s) {re l=strlen(s);for(re i=0;i<l;i++,*s++)sr[++C]=*s;sr[++C]='
    ';}
    	in void ot() {fwrite(sr,1,C+1,stdout);C=-1;}
    };
    using namespace fast_io;
    const int N=1e5+5;
    const ll inf=1e18;
    int cnt=1,sum,tot,n,s,t,m,k;
    int h[N],l,r,q[N],vis[N],a[N];
    ll ans,maxflow,dis[N];
    struct did{int u,next,to,f,w;}e[N*21];
    in void add(re a,re b,re c,re d)
    {
    	e[++cnt]=(did){a,h[a],b,c,d},h[a]=cnt;
    	e[++cnt]=(did){b,h[b],a,0,-d},h[b]=cnt;
    }
    int spfa()
    {
    	memset(vis,0,sizeof(vis));fill(dis+1,dis+sum+1,inf); 
    	dis[s]=0;queue<int>q;q.push(s);vis[s]=1;
    	while(!q.empty())
    	{
    		re i=q.front();vis[i]=0;q.pop();
    		for(re j=h[i],k;k=e[j].to,j;j=e[j].next)
    		if(e[j].f&&dis[k]>dis[i]+e[j].w)
    		{
    			dis[k]=dis[i]+e[j].w;
    			if(!vis[k]) q.push(k),vis[k]=1;
    		}
    	}	
    	return dis[t]<inf;
    }
    in int dfs(re u,re f)
    {
    	if(u==t) return f; vis[u]=1;
    	re res=0;
    	for(re i=h[u],v;v=e[i].to,i&&res<f;i=e[i].next)
    	if(e[i].f&&!vis[v]&&dis[v]==dis[u]+e[i].w)
    	{
    		re t=dfs(v,min(f-res,e[i].f));
    		res+=t;ans+=(ll)e[i].w*t;
    		e[i].f-=t;e[i^1].f+=t;
    	}
    	if(!res) dis[u]=inf;
    	return vis[u]=0,res;
    }
    in void zkw() {while(spfa()) memset(vis,0,sizeof(vis)),maxflow+=dfs(s,1e9);}
    void link(re l,re r)
    {
    	static int t[N];
    	if(l==r) return; re mid=(l+r)>>1,tot=0;
    	link(l,mid);link(mid+1,r); 
    	for(re i=l;i<=r;i++) t[++tot]=a[i];
    	sort(t+1,t+tot+1);tot=unique(t+1,t+tot+1)-t-1;
    	for(re i=1;i<tot;i++) add(sum+i,sum+i+1,1e9,t[i+1]-t[i]),add(sum+i+1,sum+i,1e9,t[i+1]-t[i]);
    	for(re i=l;i<=r;i++)
    	{
    		re j=lower_bound(t+1,t+tot+1,a[i])-t;
    		(i<=mid)?add(sum+j,n+i,1,0):add(i,sum+j,1,0); 
    	}
    	sum+=tot;
    }
    int main()
    {
    	n=read();m=read();s=0,t=sum=n*2+1;
    	for(re i=1;i<=n;i++) a[i]=read(),add(s,i,1,0),add(i,t,1,m),add(n+i,t,1,0);
    	link(1,n);zkw();write(ans,'
    ');
    	return ot(),0;
    }
    
  • 相关阅读:
    javascript 自定义事件
    javascript 实现HashTable(哈希表)
    NHibernate输出SQL语句
    Asp.net MVC Comet推送
    MySQL 数据备份与还原
    Mysql -- 慢查询
    cookie 的HttpOnly 和 Secure 属性
    Ubuntu -- 反射shell nc
    docker 访问宿主机网络
    k8s 配置文件 详解
  • 原文地址:https://www.cnblogs.com/disangan233/p/11303165.html
Copyright © 2011-2022 走看看