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;
    }
    
  • 相关阅读:
    数据库表结构变动发邮件脚本
    .net程序打包部署
    无法登陆GitHub解决方法
    netbeans 打包生成 jar
    第一次值班
    RHEL6 纯命令行文本界面下安装桌面
    C语言中格式化输出,四舍五入类型问题
    I'm up to my ears
    How to boot ubuntu in text mode instead of graphical(X) mode
    the IP routing table under linux@school
  • 原文地址:https://www.cnblogs.com/autoint/p/10266327.html
Copyright © 2011-2022 走看看