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

    题意

    一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 (A) 和区域 (B)

    每一块区域沿着河岸都建了恰好 (1000000001) 栋的建筑,每条岸边的建筑都从 (0) 编号到 (1000000000)。相邻的每对建筑相隔 (1) 个单位距离,河的宽度也是 (1) 个单位长度。区域 (A) 中的 (i) 号建筑物恰好与区域 (B) 中的 (i) 号建筑物隔河相对。

    城市中有 (N) 个居民。第 (i) 个居民的房子在区域 (P_i)(S_i) 号建筑上,同时他的办公室坐落在 (Q_i) 区域的 (T_i) 号建筑上。一个居民的房子和办公室可能分布在河的两岸,这样他就必须要搭乘船只才能从家中去往办公室,这种情况让很多人都觉得不方便。为了使居民们可以开车去工作,政府决定建造不超过 (K) 座横跨河流的大桥。

    由于技术上的原因,每一座桥必须刚好连接河的两岸,桥梁必须严格垂直于河流,并且桥与桥之间不能相交。

    当政府建造最多 (K) 座桥之后,设 (D_i) 表示第 (i) 个居民此时开车从家里到办公室的最短距离。请帮助政府建造桥梁,使得 (D_1 + D_2 + cdots + D_N) 最小。

    (1 leq K leq 2 , 1 leq N leq 10^5)

    分析

    参照租酥雨的题解。

    这题看上去很不可做呀,但是很明显K≤2这个条件是非常重要的。

    先考虑K=1怎么做。

    首先,如果一个人的房子和办公室位于河的同一侧,那么这个人肯定是不受任何影响的,即他对答案的贡献永不变。我们先把这种人预处理了,然后剩下的全是必须要过河的。

    那么现在有cnt个人要过河,第i个人要从Ai走到Bi,或者说,从Ai走到桥的位置pos,再从桥的位置pos走到Bi。说白了我们就是要求∑abs(Ai−pos)+abs(Bi−pos),可发现Ai与Bi其实无差别。

    所以就把所有的Ai跟Bi放在一起排序,然后桥的位置就一定是排序后中位数的位置。暴力统计每个点到桥的距离,这样K=1就做完了。

    现在来考虑K=2。

    首先看这样一个结论:对每个人来说,他一定会走那座离(Ai+Bi)/2更近的桥。这个结论其实不需要证明,手玩一下即可。

    那么我们把所有人按照(Ai+Bi)/2排序,那么一定是左边一部分人走左边的那座桥,右边的一部分人走右边的那座桥。

    所以我们要对左右分别维护一个数据结构,支持快速插入、删除元素,并可以快速查询中位数、查询区间和。在这里splay和权值线段树都是不错的选择。

    那么这题就做完啦。嗯哼。

    我用函数式Treap实现,时间复杂度(O(n log n))

    某神犇:好题之所以为好题,不仅要题好,而且数据要好。

    那么这题的确是好题了,不仅有卡常数据,而且还有卡RE的小数据。

    代码

    开始我信心满满地交了几遍

    #include<bits/stdc++.h>
    #define rg register
    #define il inline
    #define co const
    template<class T>il T read()
    {
    	rg T data=0;
    	rg int w=1;
    	rg char ch=getchar();
    	while(!isdigit(ch))
    	{
    		if(ch=='-')
    			w=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch))
    	{
    		data=data*10+ch-'0';
    		ch=getchar();
    	}
    	return data*w;
    }
    template<class T>il T read(rg T&x)
    {
    	return x=read<T>();
    }
    typedef long long ll;
    using std::cerr;
    using std::endl;
    
    co int N=1e5+100;
    int root[2];
    int tot;
    namespace  T
    {
    	int ch[N<<2][2],siz[N<<2];
    	ll val[N<<2],sum[N<<2];
    	int pri[N<<2];
    	
    	il int newnode(rg ll v)
    	{
    		int x=++tot;
    		ch[x][0]=ch[x][1]=0,siz[x]=1;
    		val[x]=sum[x]=v;
    		pri[x]=rand();
    		return x;
    	}
    	
    	il void pushup(rg int x)
    	{
    		siz[x]=siz[ch[x][0]]+1+siz[ch[x][1]];
    		sum[x]=sum[ch[x][0]]+val[x]+sum[ch[x][1]];
    	}
    	
    	il void split(rg int x,rg ll v,rg int&l,rg int&r)
    	{
    		if(!x)
    		{
    			l=r=0;
    			return;
    		}
    		if(val[x]<=v)
    		{
    			l=x;
    			split(ch[l][1],v,ch[l][1],r);
    			pushup(l);
    		}
    		else
    		{
    			r=x;
    			split(ch[r][0],v,l,ch[r][0]);
    			pushup(r);
    		}
    	}
    	
    	il int merge(rg int x,rg int y)
    	{
    		if(!x||!y)
    			return x+y;
    		if(pri[x]>pri[y])
    		{
    			ch[x][1]=merge(ch[x][1],y);
    			pushup(x);
    			return x;
    		}
    		else
    		{
    			ch[y][0]=merge(x,ch[y][0]);
    			pushup(y);
    			return y;
    		}
    	}
    	
    	il void insert(rg int&t,rg int z)
    	{
    		rg int x,y;
    		split(t,val[z],x,y);
    		t=merge(x,merge(z,y));
    	}
    	
    	il int erase(rg int&t,rg ll v)
    	{
    		rg int x,y,z;
    		split(t,v-1,x,y);
    		split(y,v,y,z);
    		rg int res=y;
    		y=merge(ch[y][0],ch[y][1]);
    		t=merge(x,merge(y,z));
    		ch[res][0]=ch[res][1]=0;
    		pushup(res);
    		return res;
    	}
    	
    	il ll kth(rg int&t,rg int k)
    	{
    		if(k==siz[ch[t][0]]+1)
    			return val[t];
    		if(k<=siz[ch[t][0]])
    			return kth(ch[t][0],k);
    		else
    			return kth(ch[t][1],k-siz[ch[t][0]]-1);
    	}
    	
    	il ll query(rg int&t)
    	{
    		if(!t)
    			return 0;
    		rg ll v=kth(t,(siz[t]+1)/2);
    		rg int x,y;
    		split(t,v,x,y);
    		rg ll res=v*siz[x]-sum[x]+sum[y]-v*siz[y];
    		t=merge(x,y);
    		return res;
    	}
    }
    using namespace T;
    using namespace std;
    
    struct node
    {
    	ll s,t;
    	
    	il bool operator<(rg co node&nd)co
    	{
    		return s+t<nd.s+nd.t;
    	}
    }x[N];
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	srand(20030506);
    	rg int k,n;
    	read(k),read(n);
    	rg ll ans=0;
    	for(rg int i=1;i<=n;++i)
    	{
    		rg char p[2],q[2];
    		rg ll s,t;
    		scanf("%s %lld %s %lld",p,&s,q,&t);
    		if(p[0]==q[0])
    		{
    			ans+=abs(s-t);
    			--n,--i;
    			continue;
    		}
    		x[i].s=s,x[i].t=t;
    	}
    	ans+=n;
    	sort(x+1,x+n+1);
    	if(k==1)
    	{
    		vector<int>v;
    		for(rg int i=1;i<=n;++i)
    		{
    			v.push_back(x[i].s);
    			v.push_back(x[i].t);
    		}
    		sort(v.begin(),v.end());
    		rg ll mid=v[(v.size()+1)/2-1];
    		for(rg int i=0;i<v.size();++i)
    			ans+=abs(mid-v[i]);	
    		printf("%lld
    ",ans);
    		return 0;
    	}
    	for(rg int i=1;i<=n;++i)
    	{
    		insert(root[0],newnode(x[i].s));
    		insert(root[0],newnode(x[i].t));
    	}
    	rg ll sum=query(root[0]);
    	for(rg int i=n;i>=1;--i)
    	{
    		insert(root[1],erase(root[0],x[i].s));
    		insert(root[1],erase(root[0],x[i].t));
    		sum=min(sum,query(root[0])+query(root[1]));
    	}
    	printf("%lld
    ",ans+sum);
    	return 0;
    }
    

    无论怎么卡常数都要TLE。

    然后看别人的题解发现只需要插入维护个sum数组记录就行了,不用删除。

    接着我的代码的确没有TLE了,然而RE了两个点。

    发现是这组数据:

    1 1
    B 457748887 B 796098674

    于是又改,终于AC了。

    #include<bits/stdc++.h>
    #define rg register
    #define il inline
    #define co const
    template<class T>il T read()
    {
    	rg T data=0;
    	rg int w=1;
    	rg char ch=getchar();
    	while(!isdigit(ch))
    	{
    		if(ch=='-')
    			w=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch))
    	{
    		data=data*10+ch-'0';
    		ch=getchar();
    	}
    	return data*w;
    }
    template<class T>il T read(rg T&x)
    {
    	return x=read<T>();
    }
    typedef long long ll;
    using std::cerr;
    using std::endl;
    
    co int N=2e5;
    int root[2];
    int tot;
    namespace  T
    {
    	int ch[N<<2][2],siz[N<<2];
    	ll val[N<<2],sum[N<<2];
    	int pri[N<<2];
    	
    	il int newnode(rg ll v)
    	{
    		int x=++tot;
    		ch[x][0]=ch[x][1]=0,siz[x]=1;
    		val[x]=sum[x]=v;
    		pri[x]=rand();
    		return x;
    	}
    	
    	il void pushup(rg int x)
    	{
    		siz[x]=siz[ch[x][0]]+1+siz[ch[x][1]];
    		sum[x]=sum[ch[x][0]]+val[x]+sum[ch[x][1]];
    	}
    	
    	il void split(rg int x,rg ll v,rg int&l,rg int&r)
    	{
    		if(!x)
    		{
    			l=r=0;
    			return;
    		}
    		if(val[x]<=v)
    		{
    			l=x;
    			split(ch[l][1],v,ch[l][1],r);
    			pushup(l);
    		}
    		else
    		{
    			r=x;
    			split(ch[r][0],v,l,ch[r][0]);
    			pushup(r);
    		}
    	}
    	
    	il int merge(rg int x,rg int y)
    	{
    		if(!x||!y)
    			return x+y;
    		if(pri[x]>pri[y])
    		{
    			ch[x][1]=merge(ch[x][1],y);
    			pushup(x);
    			return x;
    		}
    		else
    		{
    			ch[y][0]=merge(x,ch[y][0]);
    			pushup(y);
    			return y;
    		}
    	}
    	
    	il void insert(rg int&t,rg int z)
    	{
    		rg int x,y;
    		split(t,val[z],x,y);
    		t=merge(x,merge(z,y));
    	}
    	
    	il int erase(rg int&t,rg ll v)
    	{
    		rg int x,y,z;
    		split(t,v-1,x,y);
    		split(y,v,y,z);
    		rg int res=y;
    		y=merge(ch[y][0],ch[y][1]);
    		t=merge(x,merge(y,z));
    		ch[res][0]=ch[res][1]=0;
    		pushup(res);
    		return res;
    	}
    	
    	il ll kth(rg int&t,rg int k)
    	{
    		if(k==siz[ch[t][0]]+1)
    			return val[t];
    		if(k<=siz[ch[t][0]])
    			return kth(ch[t][0],k);
    		else
    			return kth(ch[t][1],k-siz[ch[t][0]]-1);
    	}
    	
    	il ll query(rg int&t)
    	{
    		if(!t)
    			return 0;
    		rg ll v=kth(t,(siz[t]+1)/2);
    		rg int x,y;
    		split(t,v,x,y);
    		rg ll res=v*siz[x]-sum[x]+sum[y]-v*siz[y];
    		t=merge(x,y);
    		return res;
    	}
    }
    using namespace T;
    using namespace std;
    
    struct node
    {
    	ll s,t;
    	
    	il bool operator<(rg co node&nd)co
    	{
    		return s+t<nd.s+nd.t;
    	}
    }x[N];
    
    ll sum[N];
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	srand(20030506);
    	rg int k,n;
    	read(k),read(n);
    	rg ll ans=0;
    	for(rg int i=1;i<=n;++i)
    	{
    		rg char p[2],q[2];
    		rg ll s,t;
    		scanf("%s %lld %s %lld",p,&s,q,&t);
    		if(p[0]==q[0])
    		{
    			ans+=abs(s-t);
    			--n,--i;
    			continue;
    		}
    		x[i].s=s,x[i].t=t;
    	}
    	ans+=n;
    	sort(x+1,x+n+1);
    	if(k==1)
    	{
    		if(!n)
    		{
    			printf("%lld
    ",ans);
    			return 0;
    		}
    		vector<int>v;
    		for(rg int i=1;i<=n;++i)
    		{
    			v.push_back(x[i].s);
    			v.push_back(x[i].t);
    		}
    		sort(v.begin(),v.end());
    		rg ll mid=v[(v.size()+1)/2-1];
    		for(rg int i=0;i<v.size();++i)
    			ans+=abs(mid-v[i]);	
    		printf("%lld
    ",ans);
    		return 0;
    	}
    	for(rg int i=1;i<=n;++i)
    	{
    		insert(root[0],newnode(x[i].s));
    		insert(root[0],newnode(x[i].t));
    		::sum[i]=query(root[0]);
    	}
    	ll sol=::sum[n];
    	for(rg int i=n;i>=1;--i)
    	{
    		insert(root[1],newnode(x[i].s));
    		insert(root[1],newnode(x[i].t));
    		sol=min(sol,::sum[i-1]+query(root[1]));
    	}
    	printf("%lld
    ",ans+sol);
    	return 0;
    }
    
  • 相关阅读:
    pdf文件怎么转换成word文档
    数据库的发展历程
    数据库的三级模式
    数据库概述
    时间序列的自回归模型—从线性代数的角度来看
    数据清洗
    sql commit的三种方式
    数据库标准语言SQL((Structured Query Language)
    正则化推导转载
    leetcode刷题
  • 原文地址:https://www.cnblogs.com/autoint/p/10266327.html
Copyright © 2011-2022 走看看