专家系统 - 二分 - 线段树

题目大意:现在有n个坐标(xi, yi),你要从中选出k个。假设你选出的全部k个坐标中,x坐标最小值为xmin,x坐标最大值为xmax,y坐标最小值为ymin,y坐标最大值为ymax。那么你得出的不确定度c=max(xmax-xmin, ymax-ymin)。你的目的是让c尽可能小。由于可能有很多种选法能够达成这一目的,你只需要输出这个最小的c即可。
题解:
二分答案,枚举上边界,每次加入删除,然后用线段树维护答案即可。

#include
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define lint long long
using namespace std;
namespace INPUT_SPACE{
    const int BS=(1<<24)+5;char Buffer[BS],*HD,*TL;
    char gc() { if(HD==TL) TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);return (HD==TL)?EOF:*HD++; }
    inline int inn()
    {
        int x=0,ch,s=1;while(((ch=gc())<'0'||ch>'9')&&ch!='-');if(ch^'-') x=ch^'0';else s=-1;
        while((ch=gc())>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^'0');return s*x;
    }
}using INPUT_SPACE::inn;
const lint INF=4000000010ll;const int N=100010;int k,id[N],rk[N],rkl[N];lint vy[N];
struct P{ lint x,y;inline bool operator<(const P &p)const { return x<p.x; } }p[N];
inline bool idcmp(int a,int b) { return p[a].y<p[b].y; }
struct segment{ int l,r,v,pt,ct;segment *ch[2]; }*rt;
int build(segment* &rt,int l,int r)
{
    rt=new segment,rt->l=l,rt->r=r,rt->v=rt->pt=rt->ct=0;
    int mid=(l+r)>>1;if(l==r) return 0;
    return build(rt->ch[0],l,mid),build(rt->ch[1],mid+1,r);
}
inline int push_up(segment* &rt) { return rt->v=max(rt->ch[0]->v,rt->ch[1]->v); }
inline int Clear(segment* &rt) { return rt->v=rt->pt=0,rt->ct=1; }
inline int update_tags(segment* &rt,int v) { return rt->v+=v,rt->pt+=v; }
inline int push_down(segment* &rt)
{
    if(rt->ct) Clear(rt->ch[0]),Clear(rt->ch[1]),rt->ct=0;
    if(rt->pt) update_tags(rt->ch[0],rt->pt),update_tags(rt->ch[1],rt->pt),rt->pt=0;return 0;
}
inline int update(segment* &rt,int s,int t,int v)
{
    int l=rt->l,r=rt->r,mid=(l+r)>>1;
    if(s<=l&&r<=t) return update_tags(rt,v);
    if(rt->pt||rt->ct) push_down(rt);
    if(t<=mid) update(rt->ch[0],s,t,v);
    else if(mid<s) update(rt->ch[1],s,t,v);
    else update(rt->ch[0],s,mid,v),update(rt->ch[1],mid+1,t,v);
    return push_up(rt);
}
inline int calc(lint c,int n)
{
    int ans=0;Clear(rt);
    for(int i=1,j=1;i<=n;rkl[id[i++]]=j) while(j<n&&vy[j]<p[id[i]].y-c) j++;
    for(int i=1,j=0;i<=n;i++)
    {
        while(j+1<=n&&p[j+1].x-p[i].x<=c) j++,update(rt,rkl[j],rk[j],1);
        ans=max(ans,rt->v);if(ans>=k) return k;update(rt,rkl[i],rk[i],-1);
    }
    return ans;
}
int main()
{
    int n=inn();k=inn();rep(i,1,n) p[i].x=inn(),p[i].y=inn();
    sort(p+1,p+n+1);rep(i,1,n) id[i]=i;sort(id+1,id+n+1,idcmp);
    rep(i,1,n) vy[i]=p[id[i]].y,rk[id[i]]=i;build(rt,1,n);
    lint L=0,R=INF,mid=(L+R)>>1;
    while(L<=R) { if(calc(mid,n)>=k) R=mid-1;else L=mid+1;mid=(L+R)>>1; }
    return !printf("%lld\n",L);
}

你可能感兴趣的:(线段树)