题解[「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\)啊