zoukankan      html  css  js  c++  java
  • 线段树例题及做题误区

      学会了一系列的线段树之后发现 除了扫描线还不是很熟之外一些操作基本上是得心应手了。

    但是仍是很菜,在此再次深有感悟 以后做题再看题解 直接剁手 我就不信不看题解自己的思路出现错误 每次都当我 有了正确的思路之时 

    却被一些 很迷的思路 误导去看题解,看完题解之后才恍然大悟 。这点需要注意!!!我想我都窥出正解了为什么不能再多想想呢?

    真的是超级没有成就感 感觉是非常难受的 好题被自己一时看了题解毁了这是我作为一个正在学习的人所极不想看见的。

    这道题还不错 对线段树是一个考察 如果能仔细思考的话应该是没有问题的。

    4个操作 区间修改 区间第k大查询 单点修改 裸的线段树。考虑 查讯第k大显然 开主席树或者权值线段树即可搞定。

    至于区间修改大可不必那么麻烦因为对一棵权值线段树进行区间修改权值没有任何的意义。

    考虑引入一个偏移量 这样我们可以不用管 什么区间修改了 直接修改偏移量即可 在输出和插入时做些修改 然后至多偏移量为

     范围就这么大 100000*2到 -100000*2 之间区间就有了(因为偏移量的存在)

    选择动态开点线段树 还是普通的线段树呢 根据题目算一下空间应该需要动态开点 主席树一波即可。

    //#include<bits/stdc++.h>
    #include<iostream>
    #include<cstdio>
    #include<iomanip>
    #include<cstring>
    #include<string>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<cctype>
    #include<utility>
    #include<set>
    #include<bitset>
    #include<queue>
    #include<stack>
    #include<deque>
    #include<map>
    #include<vector>
    #include<ctime>
    #define INF 2147483646
    #define ll long long
    #define db double
    #define l(i) t[i].l
    #define r(i) t[i].r
    #define sum(i) t[i].sum
    #define add(i) t[i].add
    using namespace std;
    char buf[1<<15],*fs,*ft;
    inline char getc()
    {
        return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    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*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void put(int x)
    {
        x<0?putchar('-'),x=-x:0;
        int num=0;char ch[50];
        while(x)ch[++num]=x%10+'0',x/=10;
        num==0?putchar('0'):0;
        while(num)putchar(ch[num--]);
        putchar('
    ');return;
    }
    const int MAXN=200000;
    int n,m,ans,num,cnt,root,sum;
    int maxx=MAXN,minn=-MAXN;
    char ch[2];
    struct wy
    {
        int l,r;
        int sum;
        int add;
    }t[(MAXN<<2)+2];
    void pushdown(int p)
    {
        add(l(p))=add(r(p))=1;
        sum(l(p))=sum(r(p))=0;
        add(p)=0;return;
    }
    void insert(int &p,int l,int r,int d)
    {
        if(!p)p=++num;
        if(l==r){sum(p)++;return;}
        int mid=(l+r)>>1;
        if(add(p))pushdown(p);
        if(d<=mid)insert(l(p),l,mid,d);
        else insert(r(p),mid+1,r,d);
        sum(p)=sum(l(p))+sum(r(p));
    }
    int ask(int p,int l,int r,int x,int y)
    {
        if(!p)return 0;
        int cnt=0;
        if(x<=l&&y>=r)
        {
            cnt+=sum(p);
            sum(p)=0;
            add(p)=1;
            return cnt;
        }
        int mid=(l+r)>>1;
        if(add(p))pushdown(p);
        if(x<=mid)cnt+=ask(l(p),l,mid,x,y);
        if(y>mid)cnt+=ask(r(p),mid+1,r,x,y);
        sum(p)=sum(l(p))+sum(r(p));
        return cnt;
    }
    int query(int p,int l,int r,int k)
    {
        if(l==r)return l;
        int mid=(l+r)>>1;
        if(add(p))pushdown(p);
        if(sum(r(p))>=k)return query(r(p),mid+1,r,k);
        else return query(l(p),l,mid,k-sum(r(p)));
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();m=read();
        //cout<<sizeof(t)<<endl;
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%s",ch+1);
            x=read();
            if(ch[1]=='I')
            {
                if(x<m)continue;
                insert(root,minn,maxx,x-ans);
                //cout<<x-ans<<endl;
                sum++;
            }
            if(ch[1]=='A')ans+=x;
            if(ch[1]=='S')
            {
                ans-=x;
                int u=0;
                u=ask(root,minn,maxx,minn,m-ans-1);
                cnt+=u;sum-=u;
            }
            if(ch[1]=='F')
            {
                if(sum<x)puts("-1");
                else put(query(root,minn,maxx,x)+ans);
            }
        }
        put(cnt);
        return 0;
    }
    View Code

    剩下的就是写代码时的细节问题了,我的细节总是挂 这点急需注意。

    这道题则是经典的 区间最小值 的一波智力题 或者是 分析答案所在点。

    当然 我是没有分析出来的 看了题解 发现 了答案所在地安 还是 没有窥出 如何判断不合法的方法。

    首先 区间的最小值 只能有一个区间 如果两个区间的RMQ 相同且两个区间并不相交 这便不合法了。

    考虑两个区间相交的情况 对于交集 如果被其他的比它小的最小值覆盖那么就是不合法的 。

    处理交集我们必须使用排序才行 这就是这道题需要二分的原因。求出最早的编号显然答案具有单调区间。

    然后考虑怎么处理第二个判断 这样想 一个RMQ较小的区间可以覆盖一个RMQ较大的区间 但是RMQ较小的却不能被RMQ较大的区间覆盖 

    这就是关键点所在了 排序从大到小排 能快速的判断出来 答案所在。

    对于每个RMQ 我们只需要取出区间 或者有区间并集一定要得到区间的并集 然后在线段树上询问是否这段区间已经被完全覆盖 如果被完全覆盖之后就是不合法的。

    那么就可以 利用从大到小这个性质来判断了。。关键是发现这个性质。

    //#include<bits/stdc++.h>
    #include<iostream>
    #include<cstdio>
    #include<iomanip>
    #include<cstring>
    #include<string>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<cctype>
    #include<utility>
    #include<set>
    #include<bitset>
    #include<queue>
    #include<stack>
    #include<deque>
    #include<map>
    #include<vector>
    #include<ctime>
    #define INF 2147483646
    #define ll long long
    #define db double
    #define z p<<1
    #define y p<<1|1
    #define l(x) t[x].l
    #define r(x) t[x].r
    #define v(x) t[x].v
    #define add(x) t[x].add
    using namespace std;
    char buf[1<<15],*fs,*ft;
    inline char getc()
    {
        return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    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*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void put(int x)
    {
        x<0?putchar('-'),x=-x:0;
        int num=0;char ch[50];
        while(x)ch[++num]=x%10+'0',x/=10;
        num==0?putchar('0'):0;
        while(num)putchar(ch[num--]);
        putchar('
    ');return;
    
    }
    const int MAXN=1000002;
    struct wy1
    {
        int l,r;
        int k;
    }s[MAXN],tmp[MAXN];
    int n,m,flag;
    struct wy
    {
        int l,r;
        int v;
        int add;
    }t[MAXN<<2];
    int cmp(wy1 q,wy1 p){return q.k>p.k;}
    void build(int p,int l,int r)
    {
        l(p)=l;r(p)=r;add(p)=-1;
        if(l==r)return;
        int mid=(l+r)>>1;
        build(z,l,mid);
        build(y,mid+1,r);
    }
    void pushdown(int p)
    {
        v(z)=(r(z)-l(z)+1)*add(p);
        v(y)=(r(y)-l(y)+1)*add(p);
        add(z)=add(y)=add(p);
        add(p)=-1;return;
    }
    int ask(int p,int l,int r)
    {
        if(l<=l(p)&&r>=r(p))return v(p);
        int cnt=0;
        int mid=(l(p)+r(p))>>1;
        if(add(p)!=-1)pushdown(p);
        if(l<=mid)cnt+=ask(z,l,r);
        if(r>mid)cnt+=ask(y,l,r);
        return cnt;
    }
    void change(int p,int l,int r,int d)
    {
        if(l<=l(p)&&r>=r(p)){add(p)=d,v(p)=(r(p)-l(p)+1)*d;return;}
        int mid=(l(p)+r(p))>>1;
        if(add(p)!=-1)pushdown(p);
        if(l<=mid)change(z,l,r,d);
        if(r>mid)change(y,l,r,d);
        v(p)=v(z)+v(y);
    }
    int check(int now)
    {
        for(int i=1;i<=now;i++)tmp[i]=s[i];
        sort(tmp+1,tmp+1+now,cmp);
        change(1,1,n,0);
        int xx,yy,u1,u2;
        u1=xx=tmp[1].l;u2=yy=tmp[1].r;//判断区间总体大小
        for(int i=2;i<=now;i++)
        {
            if(tmp[i].k==tmp[i-1].k)
            {
                xx=min(xx,tmp[i].l);
                yy=max(yy,tmp[i].r);
                u1=max(u1,tmp[i].l);
                u2=min(u2,tmp[i].r);
            }
            else
            {
                if(u1>u2)return 0;
                if(ask(1,u1,u2)==(u2-u1+1))return 0;
                change(1,xx,yy,1);
                xx=u1=tmp[i].l;
                yy=u2=tmp[i].r;
            }
        }
        if(u1>u2)return 0;
        if(ask(1,u1,u2)==(u2-u1+1))return 0;
        return 1;
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();m=read();
        build(1,1,n);
        for(int i=1;i<=m;i++)
        {
            s[i].l=read();
            s[i].r=read();
            s[i].k=read();
        }
        int l=1,r=m+1;
        while(l+1<r)
        {
            int mid=(l+r)>>1;
            if(check(mid)==0)r=mid;
            else l=mid;
        }
        put(r==m+1?0:r);
        return 0;
    }
    View Code

  • 相关阅读:
    火狐浏览器标签之间切换的快捷键
    LeetCode 69. x 的平方根
    LeetCode 51. N皇后
    win 10 自带 Ubuntu 系统的文件位置
    LeetCode 122. 买卖股票的最佳时机 II
    LeetCode 169. 求众数
    LeetCode 50. Pow(x, n)
    LeetCode 236. 二叉树的最近公共祖先
    LeetCode 235. 二叉搜索树的最近公共祖先
    LeetCode 98. 验证二叉搜索树
  • 原文地址:https://www.cnblogs.com/chdy/p/10546287.html
Copyright © 2011-2022 走看看