zoukankan      html  css  js  c++  java
  • 题解[「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
    ",ans);
    	return 0;
    }
    

    完结撒花❀

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

  • 相关阅读:
    iris数据集
    codevs 1262 不要把球传我 2012年CCC加拿大高中生信息学奥赛
    codevs 1742 爬楼梯(水题日常)
    codevs 2277 爱吃皮蛋的小明(水题日常)
    洛谷 P3386 【模板】二分图匹配
    vijos 1190 繁忙的都市
    codevs 1487 大批整数排序(水题日常)
    洛谷 P2820 局域网
    codevs 1683 车厢重组(水题日常)
    codevs 1228 苹果树
  • 原文地址:https://www.cnblogs.com/xxbbkk/p/14553197.html
Copyright © 2011-2022 走看看