zoukankan      html  css  js  c++  java
  • [NOI2016]区间

    题目大意:

    给你N个区间,选出M个,
    使这m个区间共同包含至少一个位置,且使里面区间长度最长减    
    区间长度最短最小

    solution:

    线段树 + 尺取法

    首先很容易想到对于这个区间覆盖了,就是让区间中的每个点加1,那就是线段树,维护区间最大值

    那么这时候就有一个问题:就是区间末尾可能很大。

    这个时候就要用到离散化:

    显然这道题只关心区间的长度,和区间之间的相互包含关系。因此可以预先把长度记录下来,把出现过的数按数值排序。 注意重复的数。。。

        for(int i=1;i<=n;i++)
        {
            l[i]=read(),r[i]=read();
            s[i].len=r[i]-l[i];s[i].num=i;//记录长度 
            ++cnt;
            b[cnt].num=i;b[cnt].val=l[i];b[cnt].bz=0;
            ++cnt;
            b[cnt].num=i;b[cnt].val=r[i];b[cnt].bz=1;
        }
        sort(b+1,b+cnt+1);//离散化
        int q=0;
        for(int i=1;i<=cnt;i++)
        {
            if(b[i].val!=b[i-1].val) q++;//如果有重复得要                                      赋成一个值
            if(b[i].bz==0)
            l[b[i].num]=q;
            else
            r[b[i].num]=q;
        }
        for(int i=1;i<=n;i++)
        {
            s[i].l=l[s[i].num];
            s[i].r=r[s[i].num];
        }//最后代回去

    离散化完了,接下来怎么做? 这个时候如果当前的最大值小于了M,显然我们需要多加区间,而已经大于了,我们可以尝试减少区间来实现更有的解。 显然这和尺取法的思想一样,即选取区间有一定规律,或者说所选取的区间有一定的变化趋势的情况时,步步推进,放缩。。。

    然后发现我们要维护最小长度,显然就按长度排序,再尺取法。。 减区间时线段树维护区间-1,加区间时维护区间+1,最后直接查询树顶。。。

    先上代码

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int MAXN = 500000 +10;
    
    const int inf = 1<<30;
    
    inline long long read()
    {
        int f=1,x=0;
        char ch;
        do
        {
            ch=getchar();
            if(ch=='-') f=-1;
        }while(ch<'0'||ch>'9');
        do
        {
            x=(x<<1)+(x<<3)+ch-'0';
            ch=getchar();
        }while(ch>='0'&&ch<='9');
        return f*x;
    }
    
    inline int Max(int a,int b)
    {
        if(a>=b) return a;
        else return b; 
    }
    
    int n,m;
    
    int l[MAXN],r[MAXN];
    
    struct stick
    {
        int len;
        int l;
        int r;
        int num;
        friend bool operator < (stick a1,stick a2)
        {
            return a1.len<a2.len;
        }
    }s[MAXN*4]; 
    
    struct node
    {
        int num;
        int val;
        bool bz;
        friend bool operator < (node a1,node a2)
        {
            return a1.val<a2.val;
        }
    };
    
    int cnt =0;
    
    node b[MAXN*4];
    
    struct stree
    {
        int l;
        int r;
        int maxv;
        inline int mid()
        {
            return (l+r)>>1;
        }
    }tree[MAXN * 8];
    
    #define lc o<<1
    #define rc o<<1|1 
    
    inline void build(int o,int l,int r)
    {
        tree[o].l=l,tree[o].r=r;
        if(l==r) tree[o].maxv=0;
        else
        {
            int mid=tree[o].mid();
            build(lc,l,mid);
            build(rc,mid+1,r);
            tree[o].maxv=Max(tree[lc].maxv,tree[rc].maxv);
            return;
        }
    } 
    
    int add[MAXN * 8];
    
    inline void pushup(int o)
    {
        add[lc]+=add[o];
        add[rc]+=add[o];
        tree[lc].maxv+=add[o];
        tree[rc].maxv+=add[o];
        add[o]=0;
    }
    
    inline void update(int o,int x,int y,int w)
    {
        int l=tree[o].l,r=tree[o].r;
        if(r<x||l>y) return;
        if(x<=l&&y>=r)
        {
            add[o]+=w;
            tree[o].maxv+=w;
            return;
        }
        else
        {
            pushup(o);
            update(lc,x,y,w);
            update(rc,x,y,w);
            tree[o].maxv=Max(tree[lc].maxv,tree[rc].maxv);
        }
    }
    
    int ans=1<<30;
    
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=n;i++)
        {
            l[i]=read(),r[i]=read();
            s[i].len=r[i]-l[i];s[i].num=i; 
            ++cnt;
            b[cnt].num=i;b[cnt].val=l[i];b[cnt].bz=0;
            ++cnt;
            b[cnt].num=i;b[cnt].val=r[i];b[cnt].bz=1;
        }
        sort(b+1,b+cnt+1);
        int q=0;
        for(int i=1;i<=cnt;i++)
        {
            if(b[i].val!=b[i-1].val) q++;
            if(b[i].bz==0)
            l[b[i].num]=q;
            else
            r[b[i].num]=q;
        }
        for(int i=1;i<=n;i++)
        {
            s[i].l=l[s[i].num];
            s[i].r=r[s[i].num];
        }
        build(1,1,q);
        sort(s+1,s+n+1);
        int li=0,ri=0;
        while(1)
        {
            while(tree[1].maxv<m&&ri<=n)
            {
                ri++; 
                update(1,s[ri].l,s[ri].r,1);
            }
            if(tree[1].maxv<m) break;
            while(tree[1].maxv>=m&&li<=ri)
            {
                li++; 
                update(1,s[li].l,s[li].r,-1);
    
            } 
                ans=min(ans,s[ri].len-s[li].len);
        }
        if(ans==inf)
        {
            printf("-1");
            return 0;
        }
        printf("%d
    ",ans);
    }

    注意

    1.第一次写的时候把建树的一个L写成了1... 然后全RE,然后非常智障的认为是数组开小了,然后非常智障的又交了几次。。。 2.第二个错是离散化后忘了这一步。。。

        for(int i=1;i<=n;i++)
        {
            s[i].l=l[s[i].num];
            s[i].r=r[s[i].num];
        }

    3.有一次在考试时做过这道题,当时很naive的写的纯模拟,因为根本没想到用线段树,然后模拟加树状数组拿了50分,其他人都拿了60分QwQ 模拟代码

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int inf = 1<<30;
    
    int n,m;
    
    struct node
    {
        int l,r;
        inline friend bool operator < (node a,node b)
        {
            return a.l<b.l;
            }
    };node t[100000+ 10];
    
    inline int read()
    {
        int f=1,x=0;
        char ch;
        do
        {
            ch=getchar();
            if(ch=='-') f=-1;
        }while(ch<'0'||ch>'9');
        do
        {
    
            x=(x<<3)+(x<<1)+ch-'0';
            ch=getchar();
        }while(ch>='0'&&ch<='9');
        return f*x;
    }
    
    int minn,maxn;
    int ans=inf;
    
    int b[500010];
    
    int c[500001];
    int lb(int x){return x&-x;};
    inline void change(int a,int b)
    {
        if(a)
        { 
        while(a<=maxn)
        {
            c[a]+=b;
            a+=lb(a);
        }
        }
    }
    inline int g_sum(int x)
    {
        int re=0;
        while(x>=1)
        {
            re+=c[x];
            x-=lb(x);
        }
        return re;
    }
    
    bool boo;
    
    int main()
    {
        n=read();
        minn=inf;
        m=read();
        for(int i=1;i<=n;i++)
        {
            t[i].l=read(),t[i].r=read();
            minn=min(minn,t[i].l);
            maxn=max(maxn,t[i].r);
            change(t[i].l,1);
            change(t[i].r+1,-1);
        }
        sort(t+1,t+n+1);
        for(register int i=minn;i<=maxn;i++)
        {
            if(g_sum(i)>=m)
            {
                boo=true;
            int num=0;
            int maxx=0,minx=inf;
            for(register int j=1;j<=n;j++)
            {
                if(i>=t[j].l&&i<=t[j].r)
                {
                    num++;
                //  cout<<b[num]<<endl;
                    b[num]=t[j].r-t[j].l;
                }
                if(t[j].l>i) break;
            }
    
                sort(b+1,b+num+1);
                int xiao = inf;
                for(register int i=1;i<=num-m+1;i++)
                {
                    if(b[i+m-1]-b[i]<xiao)
                        xiao=b[i+m-1]-b[i];
                }
                ans=min(ans,xiao);
            /*  for(int i=1;i<=num;i++)
                {
                    cout<<b[i]<<" "<<endl;
                }*/
    
        }
        }
        if(ans!=inf)
        cout<<ans<<endl;
        else cout<<-1<<endl;
    }
  • 相关阅读:
    百度影音盒插入论坛帖子自动播放代码及方法
    vFloppy1.5-虚拟启动软盘
    飞秋的实现原理
    博客盈利请先考虑这七点
    下载站运行广告合作exe文件然后再运行程序文件的bat
    木马病毒是什么以及手工清除木马病毒具体步骤
    网站盈利模式分析分类
    软件更新原理
    浅析php学习的路线图
    网页常用分享代码大全(前端必备)
  • 原文地址:https://www.cnblogs.com/wlzs1432/p/8849527.html
Copyright © 2011-2022 走看看