题解[「ROI 2019 Day2」课桌 ]

题目

Loj3192「ROI 2019 Day2」课桌

不会概括

题面很简洁的,一下就能看懂。

Sol

Step1:去重+排序

我们知道:如果一种课桌的区间被另一种完全包含,那我们就可以去掉它。

这也算是一种重复。

然后根据左端点排序。假设你是一个区间,由于这时不存在左端点比你大并且右端点比你小的区间,所以此时右端点也是有序的。

Step2:读入+排序

贪心地想,一个班的同学在排序后,身高相邻的两位同学一定是坐一张桌子。

由于人的身高单调递增,所以选的桌子的左端点一定是单调递增的,这样才能满足差量最小的需求。

Step3:分治求解

我们可以保证一个班内的人身高递增,但我们不能保证班与班之间的身高差不大。

所以我们每次为最中间的那一些同学找到桌子,再利用决策单调性分治求解。

这里的决策单调性,就是选的桌子区间单调递增的性质。

看代码更好理解。

inline void solo(int l,int r,int zl,int zr){
	if(l>r||zl>zr) return;//不合法就返回
	int mid=(l+r)>>1,tot=0,pos;	
  //找到当前每个班中 中间的那一些同学
	ll res=1e18;
	for(int i=1;i<=m;i++) v[++tot]=a[((i-1)*(n<<1))+(mid<<1)-1],v[++tot]=a[((i-1)*(n<<1))+(mid<<1)];
  //中间的那些人(成对出现)
	sort(v+1,v+1+tot);
	for(int i=1;i<=tot;i++) sum[i]=sum[i-1]+v[i];
	for(int i=zl;i<=zr;i++){
		int nl=lower_bound(v+1,v+1+tot,z[i].l)-v;//目前的点中舒适值小于左端点的位置
		int nr=upper_bound(v+1,v+1+tot,z[i].r)-v-1;//大于桌子右端点
		ll now=((ll)(nl-1)*z[i].l-sum[nl-1])+((sum[tot]-sum[nr])-(ll)(tot-nr)*z[i].r);
      //算代价
		if(now<res) res=now,pos=i;
      //找到最优的桌子的位置
	}
	ans+=res;
	solo(l,mid-1,zl,pos),solo(mid+1,r,pos,zr);
  //mid这一些人已经找好桌子了,递归[l,mid-1][mid+1,r]
  //mid这些人用pos这种桌子,前面的人用小的桌子,后面的人用大的桌子
	return;
}

ps

代码里数组名带"z"的是和桌子有关的变量。

Code

#include<bits/stdc++.h>
#define ll long long
#define N (200010)
#define M (400010)
using namespace std;
struct xbk{int l,r;}z[N],zz[N];
int n,m,k,cnk;
ll ans,v[M],sum[M],a[M];
inline ll read(){
	ll w=0;
	char ch=getchar();
	while(ch>'9'||ch<'0') ch=getchar();
	while(ch>='0'&&ch<='9'){
		w=(w<<3)+(w<<1)+(ch^48);
		ch=getchar();
	}
	return w;
}
inline void solo(int l,int r,int zl,int zr){
	if(l>r||zl>zr) return;
//	cout<<l<<" "<<r<<" "<<zl<<" "<<zr<<endl;
	int mid=(l+r)>>1,tot=0,pos;	
	ll res=1e18;
	for(int i=1;i<=m;i++) v[++tot]=a[((i-1)*(n<<1))+(mid<<1)-1],v[++tot]=a[((i-1)*(n<<1))+(mid<<1)];
	sort(v+1,v+1+tot);
//	puts("V");
//	for(int i=1;i<=tot;i++) cout<<v[i]<<" ";
//	puts("");
	for(int i=1;i<=tot;i++) sum[i]=sum[i-1]+v[i];
	for(int i=zl;i<=zr;i++){
		int nl=lower_bound(v+1,v+1+tot,z[i].l)-v;
		int nr=upper_bound(v+1,v+1+tot,z[i].r)-v-1;
		ll now=((ll)(nl-1)*z[i].l-sum[nl-1])+((sum[tot]-sum[nr])-(ll)(tot-nr)*z[i].r);
		if(now<res) res=now,pos=i;
	}
	ans+=res;
	solo(l,mid-1,zl,pos),solo(mid+1,r,pos,zr);
	return;
}
inline bool cmp(xbk a,xbk b){
	if(a.l!=b.l) return a.l<b.l;
	return a.r>b.r;
}
int main(){
	m=read(),n=read(),k=read();
	for(int i=1;i<=k;i++)
		zz[i].l=read(),zz[i].r=read();
	sort(zz+1,zz+1+k,cmp);
	for(int i=1,j;i<=k;i=j){
		z[++cnk]=zz[i];
		j=i+1;
		while(zz[j].r<=z[cnk].r&&j<=k) j++;
	}
//	puts("z");
//	for(int i=1;i<=cnk;i++) cout<<z[i].l<<" "<<z[i].r<<endl;
	for(int i=1;i<=m;i++){
		for(int j=1;j<=(n<<1);j++) a[(i-1)*(n<<1)+j]=read();
		sort(a+(i-1)*(n<<1)+1,a+i*(n<<1)+1);
//		cout<<(i-1)*(n<<1)+1<<" "<<i*(n<<1)+1<<endl;
	}
//	puts("Students");
//	for(int i=1;i<=2*n*m;i++) cout<<a[i]<<" ";
//	puts("");
	solo(1,n,1,cnk);
	printf("%lld\n",ans);
	return 0;
}

完结撒花❀

话说为什么身高可以到\(10^9\)

posted @ 2021-03-18 07:21  xxbbkk  阅读(121)  评论(0编辑  收藏  举报