[线段树 中位数] BZOJ 4071 [Apio2015]巴邻旁之桥

首先如果办公室和家在同一侧,直接将距离加到答案中即可。

如果办公室和家不在同一侧

对于k=1,显然只需要将桥建在所有位置的中位数即可。

对于k=2,可以发现每个人都会选择距离家和办公室中点较近的桥行走。那么我们就可以按照家和办公室中点将每个人排序,枚举分割点,将分割点前后的人分别处理。在权值线段树上二分求中位数


#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;

inline char nc()
{
	static char buf[100000],*p1=buf,*p2=buf;
	if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
	return *p1++;
}

inline void read(int &x)
{
	char c=nc(),b=1;
	for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
	for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

inline void read(char &x)
{
	for (x=nc();x!='A' && x!='B';x=nc());
}

const int N=200005;

int n,K,tot;
int icnt,sx[N];
ll ans,all_add;

inline int Bin(int x){
	return lower_bound(sx+1,sx+icnt+1,x)-sx;
}

inline int dist(int x,int y){
	return abs(sx[x]-sx[y]);
}

struct abcd{
	int l,r;
	abcd(int l=0,int r=0):l(l),r(r) { }
	bool operator < (const abcd &B) const {
		return l+r<B.l+B.r;
	}
}a[N];

struct SEGTREE{
	int M,TH;
	int T[N*4]; ll H[N*4];
	inline void Build(int n){
		for (M=1,TH=0;M<n+2;TH++,M<<=1);
	}
	inline void add(int s,int r,ll x){
		T[s+=M]+=r; H[s]+=r*x;
		while (s>>=1)
			T[s]+=r,H[s]+=r*x;
	}
	inline ll sum(int s,int t){
		if (s>t) return 0;
		ll ret=0;
		for (s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1)
		{
			if (~s&1) ret+=H[s^1];
			if ( t&1) ret+=H[t^1];
		}
		return ret;
	}
	inline ll cnt(int s,int t){
		if (s>t) return 0;
		ll ret=0;
		for (s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1)
		{
			if (~s&1) ret+=T[s^1];
			if ( t&1) ret+=T[t^1];
		}
		return ret;
	}
	inline int Mid(){
		int K=T[1]/2+1,i;
		for (i=1;i<M;)
			if (T[i<<1]<K) 
				K-=T[i<<1],i=i<<1|1;
			else
				i=i<<1;
		return i-M;
	}
}SEG1,SEG2;

inline ll Solve(int m){
	ll ret=0,mid;
	if (m)
	{
		mid=SEG1.Mid();
		ret+=m;
		ret+=SEG1.cnt(1,mid-1)*sx[mid]-SEG1.sum(1,mid-1);
		ret+=SEG1.sum(mid+1,icnt)-SEG1.cnt(mid+1,icnt)*sx[mid];
	}
	if (n-m)
	{
		mid=SEG2.Mid();
		ret+=tot-m;
		ret+=SEG2.cnt(1,mid-1)*sx[mid]-SEG2.sum(1,mid-1);
		ret+=SEG2.sum(mid+1,icnt)-SEG2.cnt(mid+1,icnt)*sx[mid];
	}
	return ret;
}

int main()
{
	char w,z; int x,y; 
	freopen("t.in","r",stdin);
	freopen("t.out","w",stdout);
	read(K); read(n);
	if (K==1)
	{
		for (int i=1;i<=n;i++)
		{
			read(w); read(x); read(z); read(y);
			if (w==z) 
				ans+=abs(x-y);
			else 
				sx[++icnt]=x,sx[++icnt]=y;
		}
		sort(sx+1,sx+icnt+1);
		x=sx[icnt/2+1];
		ans+=icnt/2;
		for (int i=1;i<=icnt;i++) 
			ans+=abs(sx[i]-x);
		printf("%lld\n",ans);
		return 0;
	}
	for (int i=1;i<=n;i++)
	{
		read(w); read(x); read(z); read(y);
		if (x>y) swap(x,y);
		if (w==z) 
			all_add+=abs(x-y);
		else 
			a[++tot]=abcd(x,y);
	}
	sort(a+1,a+tot+1);
	for (int i=1;i<=tot;i++) sx[++icnt]=a[i].l,sx[++icnt]=a[i].r;
	sort(sx+1,sx+icnt+1);
	icnt=unique(sx+1,sx+icnt+1)-sx-1; 
	SEG1.Build(icnt); SEG2.Build(icnt);
	for (int i=1;i<=tot;i++) a[i].l=Bin(a[i].l),a[i].r=Bin(a[i].r);
	ans=1LL<<60;
	for (int i=1;i<=tot;i++) 
		SEG2.add(a[i].l,1,sx[a[i].l]),SEG2.add(a[i].r,1,sx[a[i].r]);
	ans=min(ans,Solve(0));
	for (int i=1;i<=tot;i++)
	{
		SEG1.add(a[i].l,1,sx[a[i].l]); SEG1.add(a[i].r,1,sx[a[i].r]);
		SEG2.add(a[i].l,-1,sx[a[i].l]); SEG2.add(a[i].r,-1,sx[a[i].r]);
		ans=min(ans,Solve(i));
	}
	printf("%lld\n",ans+all_add);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值