【JZOJ100005】【NOI2017模拟.4.1】Shoes

本文介绍了一种使用主席树和分治法解决鞋柜分配问题的方法。通过将鞋按中点排序,并利用主席树维护鞋的分布情况,可以有效计算出最小的分配成本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

任务

这里写图片描述
这里写图片描述

解法

我们考虑将每双鞋按两鞋的中点排序,然后把鞋子放的就是一段连续的区间了。
现在我们设f[i][j]表示前i双鞋子,用了j个鞋柜,所需要的最小代价,就有

fi,j=mink<i{fk,j1+w(k+1,i)}

其中w(k+1,i)表示把第k+1i双鞋放在一个鞋柜中的最小代价。
显然我们要求w(k+1,i)的话,就要知道第k+1i双鞋的分布情况,这个可以用主席树来维护。
知道了分布情况之后,我们再主席树中,找出中位数位置;
知道鞋柜位置之后,然后我们就再利用主席数,统计每只鞋子到这个位置的距离和。
这样的时间复杂度为O(n2klog)

如果我们先枚举j,再枚举i,我们发现fi,j的决策点k不会小于fi,j的决策点k,其中i<i
那么我们就可以利用决策单调性来维护。
我这里采用的是分治法:
先算出fmid,i的决策点kmid,那么对于左右区间而言,枚举的决策点就可以减少了。


总的时间复杂度为O(nklog2)

代码

#include<iostream>
#include<algorithm>
#include<math.h>
#include<stdio.h>
#include<string.h>
#define ll long long
#define fo(i,x,y) for(ll i=x;i<=y;i++)
#define f(x,y) f[(x)*(m+1)+(y)]
using namespace std;
const char* fin="shoes.in";
const char* fout="shoes.out";
const ll inf=0x7fffffff;
const ll maxn=200007,maxt=maxn*60;
ll n,m,f[maxn],num,last,rt[maxn],ans,lim;
struct shoes{
    ll x,y;
}a[maxn];
bool cmp(shoes a,shoes b){return (a.x+a.y)/2.0<(b.x+b.y)/2.0;}
struct node{
    ll x,l,r,lson,rson;
}c[maxt];
void modify(ll l,ll r,ll t,ll v,ll _t){
    ll mid=(l+r)/2;
    if (l==r){
        c[t].x=c[_t].x+1;
        return;
    }
    if (v<=mid){
        c[t].rson=c[_t].rson;
        modify(l,mid,c[t].lson=++num,v,c[_t].lson);
    }else{
        c[t].lson=c[_t].lson;
        modify(mid+1,r,c[t].rson=++num,v,c[_t].rson);
    }
    c[t].x=c[c[t].lson].x+c[c[t].rson].x;
    c[t].l=c[c[t].rson].l+c[c[t].lson].l+c[c[t].rson].x*(mid-l+1);
    c[t].r=c[c[t].lson].r+c[c[t].rson].r+c[c[t].lson].x*(r-mid);
}
ll query(ll l,ll r,ll t,ll v,ll _t){
    ll mid=(l+r)/2;
    if (r<=v) return c[t].r-c[_t].r+(c[t].x-c[_t].x)*(v-r);
    if (l>=v) return c[t].l-c[_t].l+(c[t].x-c[_t].x)*(l-v);
    return query(l,mid,c[t].lson,v,c[_t].lson)+query(mid+1,r,c[t].rson,v,c[_t].rson);
}
ll getmid(ll l,ll r,ll t,ll _t,ll ls,ll rs){
    ll mid=(l+r)/2,L=c[c[t].lson].x-c[c[_t].lson].x+ls,R=c[c[t].rson].x-c[c[_t].rson].x+rs;
    if (l==r) return l;
    if (L<R) return getmid(mid+1,r,c[t].rson,c[_t].rson,L,rs);
    else if (L>R) return getmid(l,mid,c[t].lson,c[_t].lson,ls,R);
    else return mid;
}
ll mincost(ll l,ll r){return query(1,2e9+1,rt[r],getmid(1,2e9+1,rt[r],rt[l],0,0),rt[l]);}
void dfs(ll j,ll l,ll r,ll L,ll R){
    ll mid=(l+r)/2,tmd=0;
    if (l>r) return;
    fo(k,L,min(mid-1,R)){
        if (f(k,j-1)<lim){
            ll tmp=f(k,j-1)+mincost(k,mid);
            if (tmp<f(mid,j)){
                f(mid,j)=tmp;
                tmd=k;
            }
            if (n==mid) ans=min(f(mid,j),ans);
        }
    }
    dfs(j,l,mid-1,L,tmd);
    dfs(j,mid+1,r,tmd,R);
}
int main(){
    freopen(fin,"r",stdin);
    freopen(fout,"w",stdout);
    scanf("%lld%lld",&n,&m);
    fo(i,1,n){
        scanf("%lld%lld",&a[i].x,&a[i].y);
        a[i].x+=1e9+1;
        a[i].y+=1e9+1;
    }
    sort(a+1,a+n+1,cmp);
    memset(f,127,sizeof(f));
    lim=f[0];
    ans=f[0];
    f(0,0)=0;
    rt[0]=0;
    fo(i,1,n){
        modify(1,2e9+1,rt[i]=++num,a[i].x,rt[i-1]);
        modify(1,2e9+1,rt[i],a[i].y,rt[i]);
    }
    fo(j,1,m)
        dfs(j,1,n,0,n);
    printf("%lld",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值