[ROI 2019 Day2]课桌

课桌

题解

首先,我们可以观察到结论,在同一组内,我们按高度排序后, a 2 i − 1 a_{2i-1} a2i1肯定会与 a 2 i a_{2i} a2i坐在同一张桌子上。
应该是很好感性理解的,因为这样可以让我们匹配的两张桌子的差值最小,我们不可能找出一种更优的匹配方案。画下图就知道了

由于我们总共只能选择 n n n张桌子,所以肯定是较小的对坐较小的桌子,较大的对坐较大的桌子。
我们该如何比较两张桌子的大小呢,由于对于区间 [ L j , R j ] ⊂ [ L i , R i ] [L_{j},R_{j}]\subset[L_{i},R_{i}] [Lj,Rj][Li,Ri],当选择区间 j j j时我们都能用区间 i i i代替,而且区间 i i i永远优于区间 j j j,所以这样被包含的区间 j j j我们是永远都不会选的。
于是,可以发现,我们所选择的区间 i , j i,j i,j应该满足 L i < L j ⩽ R i < R j L_{i}<L_{j}\leqslant R_{i}<R_{j} Li<LjRi<Rj的性质。
这样,对于每组选择好了的桌子的情况,每对人所坐的桌子也是固定的。
不同组的 a 2 i − 1 a_{2i-1} a2i1 a 2 i a_{2i} a2i肯定都坐的是同一张桌子,所以我们可以把这些人提取出来,对他们单独找出最优的一张桌子。
找桌子的过程我们可以将所有的桌子排序后用双指针进行维护,时间复杂度是 O ( n ( m + k ) ) O\left(n(m+k)\right) O(n(m+k))。的,显然是过不了的。

但我们可以考虑我们上面的性质:较小的对坐较小的桌子,较大的对坐较大的桌子,也就是我们选择的桌子是单调的。
于是,我们可以考虑通过二分来对其维护。
每次二分区间对 ( [ l , r ] , [ a l , a r ] ) ([l,r],[al,ar]) ([l,r],[al,ar])表示区间 [ l , r ] [l,r] [l,r]中的配对应该在区间 [ a l , a r ] [al,ar] [al,ar]中找到自己所匹配的桌子,如果我们的第 m i d mid mid对匹配的桌子 k k k,那么我们就应该下传到区间对 ( [ l , m i d − 1 ] , [ a l , k ] ) ([l,mid-1],[al,k]) ([l,mid1],[al,k])与区间对 ( [ m i d + 1 , r ] , [ a r , k ] ) ([mid+1,r],[ar,k]) ([mid+1,r],[ar,k])中。
显然,它后面的不可能选比它还低的桌子,它前面的也不可能选比它还高的桌子,所以我们显然是可以这样二分的。
总共有 l o g log log层,而每层会覆盖区间 [ 1 , k ] [1,k] [1,k]一次,这显然是 log ⁡   n \log\,n logn级别的。
将所有桌子排完序后跑一遍就可以了。

时间复杂度 O ( n m + ( m + k ) log ⁡   n ) O\left(nm+(m+k)\log\,n\right) O(nm+(m+k)logn)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
const LL INF=0x3f3f3f3f3f3f3f3f;
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x>9)print(x/10);putchar(x%10+'0');}
int n,m,k,cnt,tmp[MAXN<<1],tot;LL ans;
struct ming{int l,r;}s[MAXN],d[MAXN];
vector<int>vec[MAXN];
bool cmp(ming x,ming y){
	if(x.l==y.l)return x.r>y.r;
	return x.l<y.l; 
}
int check(int ai,int al,int ar){
	for(int i=1;i<=m;i++)
		tmp[++tot]=vec[i][ai+ai],
		tmp[++tot]=vec[i][ai+ai+1];
	sort(tmp+1,tmp+tot+1);
	int jl=0,jr=0,rk;LL res=INF,suml=0,sumr=0,summ=0;
	for(int i=1;i<=tot;i++)summ+=tmp[i];
	for(int i=al;i<=ar;i++){
		while(jl<tot&&tmp[jl+1]<=d[i].l)jl++,suml+=tmp[jl];
		while(jr<tot&&tmp[jr+1]<=d[i].r)jr++,sumr+=tmp[jr];
		LL tmp=1ll*jl*d[i].l-suml+summ-sumr-1ll*(tot-jr)*d[i].r;
		if(tmp<res)res=tmp,rk=i;
	}
	ans+=res;tot=0;return rk;
}
void sakura(int l,int r,int al,int ar){
	int mid=l+r>>1,mi=check(mid,al,ar);
	if(l<mid)sakura(l,mid-1,al,mi);
	if(r>mid)sakura(mid+1,r,mi,ar);
}
int main(){
	read(m);read(n);read(k);
	for(int i=1,l,r;i<=k;i++)
		read(l),read(r),s[i]=(ming){l,r};
	for(int i=1,x;i<=m;i++)
		for(int j=1;j<=n+n;j++)
			read(x),vec[i].pb(x);
	for(int i=1;i<=m;i++)sort(vec[i].begin(),vec[i].end());
	sort(s+1,s+k+1,cmp);int lst=0;cnt=0;
	for(int i=1;i<=k;i++)if(s[i].r>lst)
		d[++cnt]=s[i],lst=s[i].r;
	sakura(0,n-1,1,cnt);
	printf("%lld\n",ans);
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值