zoukankan      html  css  js  c++  java
  • [APIO2015]八邻旁之桥

    题面在这里

    sol

    这是一个(Splay)的题解
    首先,如果一个人的家和办公室在同一侧,我们可以直接预处理;
    如果不在同一侧,也可以加上1(当然要过桥啦)

    当k==1时

    我们设桥的位置为(pos),每个人的家的位置为(x[i]),办公室的位置为(y[i]),
    则总代价为(sum_{i=1}^n (abs(x_i-pos)+abs(y_i-pos)))
    从这里我们可以看到,其实家和办公室的区别不是很明显。
    所以这个问题可以简化为:
    在数轴上任取一点a,最小化 (sum abs(a-x_i))
    那么我们将所有家和办公室按照坐标排序,桥的位置肯定就在中间两个端点的位置之间
    至于怎么统计相信大家都会吧(就在下面)
    把所有家和办公室的坐标丢进一棵Splay中,平分
    统计出左边的sum和右边的sum,左边的sz和右边的sz

    当k==2时

    再将上面的式子细分一下,我们能发现:
    对于每条路径(x[i]->y[i]),其实际长度和(1/2(x[i]+y[i]))距离桥的距离有关
    于是我们可以考虑将所有路径按照((x[i]+y[i]))排序
    考虑建立两颗Splay
    首先把所有的节点全部插入一棵Splay中去
    然后一对一对(注意此处)的从老Splay中丢到另一棵Splay中去,一边统计答案
    复杂度是O(nlogn)
    应该是做完了

    什么!!!!你被卡常了!!!!

    在下提交的时候,第二个点总是TLE...交了无数遍的95分
    看着机房的其他dalao都是用线段树做的,并不是很甘心啊......
    但是方法总比困难多(hhhh)
    具体可以参考我的代码
    (一遍过的dalao可以略过)

    代码

    #include<iostream>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define RG register
    #define isr(i) (s[1][fa[(i)]]==(i))
    
    using namespace std;
    typedef long long ll;
    const int N=200010;
    const int inf=2147483647;
    int cntt;
    ll Ans[N];
    
    struct line{
    	int l,r;
    	bool operator <(const line &a)const{
    		return (l+r)<(a.l+a.r);
    	}
    }t[N];
    
    inline int read()
    {
    	RG int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    
    bool cmp(line a,line b){return (a.l+a.r)<(b.l+b.r);}
    
    struct Splay{
    	int root,tot,j,k;
    	int s[2][N],fa[N],sz[N],cnt[N];
    	ll sum[N],v[N];
    
    	inline bool empty(){return !(bool)sz[root];}
    
    	inline void clear(){
    		root=tot=0;
    		memset(s,0,sizeof(s));
    		memset(fa,0,sizeof(fa));
    		memset(sz,0,sizeof(sz));
    		memset(sum,0,sizeof(sum));
    		memset(cnt,0,sizeof(cnt));
    		memset(v,0,sizeof(v));
    	}
        
    	inline void init(int i,int x,int ff){
    		s[0][i]=s[1][i]=0;fa[i]=ff;
    		v[i]=sum[i]=x;cnt[i]=sz[i]=1;
    	}
        
    	inline void update(int i){
    		sz[i]=sz[s[0][i]]+sz[s[1][i]]+cnt[i];
    		sum[i]=sum[s[0][i]]+sum[s[1][i]]+cnt[i]*v[i];
    	}
    
    	inline void rot(int i){
    		j=fa[i];k=fa[j];
    		RG bool b=isr(i);
    		fa[i]=k;s[isr(j)][k]=i;
    		if(s[!b][i])fa[s[!b][i]]=j;s[b][j]=s[!b][i];
    		fa[j]=i;s[!b][i]=j;
    		update(j);
    	}
    
    	inline void splay(int i,int a){
    		if(!a)root=i;
    		while(fa[i]^a){
    			j=fa[i];
    			if(fa[j]^a)
    				isr(i)^isr(j)?rot(i):rot(j);
    			rot(i);
    		}
    		update(i);
    	}
    
    	inline void insert(int x){
    		RG int i=root,ff=0;
    		while(v[i]!=x&&i){
    			ff=i;i=s[v[i]<x][i];
    		}
    		if(i&&v[i]==x)cnt[i]++;
    		else{
    			i=++tot;
    			if(ff)s[v[ff]<x][ff]=i;
    			init(i,x,ff);
    			if(x==inf||x==-inf){
    				sz[i]=sum[i]=cnt[i]=0;
    			}
    		}
    		splay(i,0);
    	}
    
    	inline int find(int x){
    		RG int i=root;
    		while(v[i]!=x&&s[v[i]<x][i])
    			i=s[v[i]<x][i];
    		return i;
    	}
        
    	inline void Next(int x,int &lst,int &nxt){
    		RG int i=find(x);splay(i,0);
    		if(v[i]>x)nxt=i;
    		else {
    			nxt=s[1][i];
    			while(s[0][nxt])nxt=s[0][nxt];
    		}
    		if(v[i]<x)lst=i;
    		else{
    			lst=s[0][i];
    			while(s[1][lst])lst=s[1][lst];
    		}
    	}
        
    	inline void Delete(int x){
    		RG int i=find(x);
    		cnt[i]--;splay(i,0);
    	}
        
    	inline int kth(int k){
    		RG int i=root;
    		while(1){
    			if(sz[s[0][i]]>=k)i=s[0][i];
    			else if(sz[s[0][i]]+cnt[i]>=k)return i;
    			else k-=sz[s[0][i]]+cnt[i],i=s[1][i];
    		}
    	}
        
    	inline ll bridge(){//这里是统计答案(不开long long可是会炸飞的)
    		if(empty())return 0;
    		int i=kth(sz[root]/2);splay(i,0);
    		return 1ll*sz[s[0][i]]*v[i]-sum[s[0][i]]+sum[s[1][i]]-1ll*sz[s[1][i]]*v[i];
    	}
    }A,B;
    
    inline ll input(ll n){
    	RG char p[5],q[5];
    	RG ll ans=0;RG int s,T;
        
    	cntt=0;
    	A.insert(inf);A.insert(-inf);
    	B.insert(inf);B.insert(-inf);
        
    	for(RG int i=1;i<=n;i++){
    		scanf("%s",p+1);s=read();
    		scanf("%s",q+1);T=read();
    		if(p[1]!=q[1]){
    			ans++;
    			t[++cntt].l=s;t[cntt].r=T;
    		}
    		else ans+=abs(s-T);
    	}
    	sort(t+1,t+cntt+1);
    	for(RG int i=1;i<=cntt;i++){
    		A.insert(t[i].l);A.insert(t[i].r);
    		Ans[i]=A.bridge();
    		/*
    			最关键的地方就是这里
    			直接记录一个Ans数组表示前i条路径全部走到一座桥上的答案
    			之后就不用删除了,把路径倒着插入另外一棵线段树中统计即可
    		 */
    	}
        
    	return ans;
    }
    
    inline void work2(int k,int n){
        
    	RG ll ans=input(n);
    	RG ll minn=Ans[cntt];
    	if(A.empty()){
    		printf("%lld
    ",ans);
    		return;
    	}
        
    	for(RG int i=cntt;i>=1;i--){
    		B.insert(t[i].l);B.insert(t[i].r);
    		minn=min(minn,Ans[i-1]+B.bridge());
    		//倒插统计部分
    	}
    
    	printf("%lld
    ",minn+ans);
    }
    
    inline void work1(int k,int n){
    	printf("%lld
    ",input(n)+A.bridge());
    }
    
    int main()
    {
    	RG int k,n;
    	k=read();n=read();
    	if(k^1)work2(k,n);
    	else work1(k,n);
    	return 0;
    }
    
  • 相关阅读:
    HyperV应用指南之4虚拟机管理[转]
    Windows Server 2003文件夹不能共享的解决办法【转】
    彻底了解DVD:从入门到精通(二)[转]
    HyperV应用指南之2--安装HyperV Server 2008 R2并配置远程管理[转]
    HyperV应用指南之HyperV应用基础[转]
    IIS7.5由于权限不足而无法读取配置文件的解决办法
    C# 十六进制字符串与数值类型之间转换(转)
    分享一个Winform下的分页控件[转]
    mysql的replace函数替换字符串功能简介
    聊聊.net程序设计——浅谈使用VS2010建模拓展(下)[转]
  • 原文地址:https://www.cnblogs.com/cjfdf/p/8403475.html
Copyright © 2011-2022 走看看