码迷,mamicode.com
首页 > 其他好文 > 详细

POJ 1201 Intervals

时间:2019-10-22 15:34:00      阅读:90      评论:0      收藏:0      [点我收藏+]

标签:分时   $1   size   style   algorithm   math   font   ace   iostream   

传送门

听说正常写法是差分约束?

我怎么看都是贪心啊,所以就用贪心写了(其实是我忘记差分约束怎么写了)

考虑把区间按右端点排序,那么对于每个区间,我们考虑选择的数尽量贴着区间右边,因为这样还可以尽量满足之后区间的要求

(显然填在左边对后面没有任何好处,填右边一定比填左边好)

然后这样搞如果直接暴力复杂度是可以卡到 $n^2$ 的,所以考虑用线段树优化一下这个贪心

线段树维护一下当前每个位置是否已经选择,然后对于每个题目限制的区间 $l,r,x$,(区间 $[l,r]$ 内要选择 $x$ 个数)

我们首先把 $x$ 先减去当前 $[l,r]$ 内已经选择的位置数量,然后现在问题就是确定位置 $p$ ,使得把当前 $[p,r]$ 内没有选择的数选择以后恰好满足限制

这个东西显然是可以二分的,然后就做完了,复杂度因为二分时要线段树查询一下所以是 $n \log ^2 max(r) $ 的,已经可以过了

但是我脑子转不过来就是要写线段树上二分

但是正常的线段树上二分是在整颗线段树 $[1,max(r)]$ 上二分,不慌,注意到此时 $(r,max(r)]$ 的所有位置都还没选

所以把原本二分要求的值加上 $max(r)-r$ 即可在整颗线段树上二分了,复杂度 $n \log max(r)$

(如果实在听不懂的话直接看代码或许比较简单?)

线段树维护区间赋值为 $1$ 时因为每个线段树节点最多被改变一次所以甚至不用打标记,直接暴力修改即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<0||ch>9) { if(ch==-) f=-1; ch=getchar(); }
    while(ch>=0&&ch<=9) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=1e5+7;
int n,m,ans;
struct dat {
    int l,r,mi;
    dat (int _l=0,int _r=0,int _mi=0) { l=_l,r=_r,mi=_mi; }
    inline bool operator < (const dat &tmp) const {
        return r!=tmp.r ? r<tmp.r : l<tmp.l;
    }
}D[N];
struct SegTree {
    int T[N<<2];
    void change(int o,int l,int r,int ql,int qr)
    {
        if(l>qr||r<ql||T[o]==r-l+1) return;
        if(l==r) { T[o]=1; return; }
        int mid=l+r>>1; change(o<<1,l,mid,ql,qr); change(o<<1|1,mid+1,r,ql,qr);
        T[o]=T[o<<1]+T[o<<1|1];
    }
    int query_sum(int o,int l,int r,int ql,int qr)
    {
        if(l>qr||r<ql) return 0;
        if(l>=ql&&r<=qr) return T[o];
        int mid=l+r>>1;
        return query_sum(o<<1,l,mid,ql,qr)+query_sum(o<<1|1,mid+1,r,ql,qr);
    }
    int query_pos(int o,int l,int r,int K)
    {
        if(l==r) return l;
        int mid=l+r>>1,val=r-mid-T[o<<1|1];
        if(K>val) return query_pos(o<<1,l,mid,K-val);
        return query_pos(o<<1|1,mid+1,r,K);
    }
}T;
int main()
{
    n=read(); int a,b,c;
    for(int i=1;i<=n;i++)
    {
        a=read(),b=read(),c=read();
        m=max(m,b); D[i]=dat(a,b,c);
    }
    sort(D+1,D+n+1);
    for(int i=1;i<=n;i++)
    {
        int t=T.query_sum(1,1,m,D[i].l,D[i].r);
        if(t>=D[i].mi) continue;
        ans+=D[i].mi-t; int p=T.query_pos(1,1,m, D[i].mi-t + m-D[i].r);
        T.change(1,1,m,p,D[i].r);
    }
    printf("%d\n",ans);
    return 0;
}

 

POJ 1201 Intervals

标签:分时   $1   size   style   algorithm   math   font   ace   iostream   

原文地址:https://www.cnblogs.com/LLTYYC/p/11719403.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!