zoukankan      html  css  js  c++  java
  • 二打可持久化线段树感想

    昨天突然脑袋比较清醒,好像似乎以前没有搞太懂的可持久化线段树一下子就搞懂了,结果打了几遍还是出现了一些意想不到的问题,下面我就来整理一下,防止以后重蹈覆辙!

    下面我放一个50分的代码:

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    struct sd{
        LL l,r,cnt,son[2];
    }node[1000005];
    const LL Max=1e9+7;
    LL ini[1000004],rot[1000004],n,m,cnt;
    void update(LL k){node[k].cnt=node[node[k].son[0]].cnt+node[node[k].son[1]].cnt;}
    /*void modify(LL k,LL l,LL r,LL pos)
    {
        node[k].l=l;node[k].r=r;LL mid=(l+r)/2;
        if(l==r) node[k].cnt++;
        else if(pos<=mid)
        {
            ++cnt;
            node[cnt]=node[node[k].son[0]];
            node[k].son[0]=cnt;
            modify(node[k].son[0],l,mid,pos);
        }
        else
        {
            ++cnt;
            node[cnt]=node[node[k].son[1]];
            node[k].son[1]=cnt;
            modify(node[k].son[1],mid+1,r,pos);
        }
        update(k);
    }*/
    LL query(LL lroot,LL rroot,LL rank)
    {
        if(node[rroot].l==node[rroot].r) 
        return node[rroot].l;//strange
        else
        {
            LL delta=node[node[rroot].son[0]].cnt-node[node[lroot].son[0]].cnt;
            if(rank<=delta) return query(node[lroot].son[0],node[rroot].son[0],rank);
            else return query(node[lroot].son[1],node[rroot].son[1],rank-delta);
        }
    }
    inline void modify(LL k,long long l,long long r,LL val)
    {
        node[k].l=l;node[k].r=r;
        if(l==r)
        {
            node[k].cnt++;return;
        }
        long long mid=l+r;mid/=2;
        cnt++;
        node[cnt]=node[node[k].son[0]];
        node[k].son[0]=cnt;cnt++;
        node[cnt]=node[node[k].son[1]];
        node[k].son[1]=cnt;
        if(val<=mid)
        {
           
            modify(node[k].son[0],l,mid,val);
        }
        else
        {
           
            
            modify(node[k].son[1],mid+1,r,val);
        }
        update(k);
    }
    
    //LL query(LL rl,LL rr,LL val)
    //{
    //    if(node[rr].l==node[rr].r) return node[rr].l;//very very important
    //    LL delta=node[node[rr].son[0]].cnt-node[node[rl].son[0]].cnt;
    //    if(val<=delta)
    //    return query(node[rl].son[0],node[rr].son[0],val);
    //    else
    //    return query(node[rl].son[1],node[rr].son[1],val-delta);
    //}
    int main()
    {
        scanf("%lld%lld",&n,&m);
        for(LL i=1;i<=n;++i) scanf("%lld",&ini[i]);
        //rot[0]=1;//important
        for(LL i=0;i<=n;++i)
        {
            rot[i]=++cnt;node[rot[i]]=node[rot[i-1]];modify(rot[i],0,2*Max,Max+ini[i]);
        }
        LL a,b,c;
        for(LL i=1;i<=m;++i)
        {
            scanf("%lld%lld%lld",&a,&b,&c);
            printf("%lld
    ",query(rot[a-1],rot[b],c)-Max);
        }
        return 0;
    }
    

    我们可以发现后面的几个点是出了问题的其实就是RE,这个原因其实很容易就可以发现,因为我们如果要查一号位置的话,然后我们就会惊讶的发现,我们会query零号根(rot[0])然后我们把0号根定义的是到1号节点,然后就出事了!!!

    所以说这里cnt应该初始化到1,或者直接改为

    rot[0]=++cnt;
    

    然后我确实是发现了这个问题,但是交上去发现还是RE

    这是为什么?

    然后我发现这次RE的原因是数组开小了,至少电脑是这样认为的,但是其实我的数组是开的够大的,是因为我的动态开点modify的时候开点开了很多没有必要的点,所以导致了空间浪费(我本来以为这样开点可以使开出来的树更加的平衡)但是事实上证明我是错的,因为大家可以好好理解一下为什么下面的query中的第一个if语句一定写的是右根而不是左根!!!

    这篇博客对于上面那个query中的小细节有所解释:
    主席树(可持久化线段树)

    然后把这里改了,在这里再注意下这个主席树节点个数要开到10000000(别数了,一千万)然后你就可以AC这道题了,坑点还是很多的!!!

    AC code:

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    struct sd{
        LL l,r,cnt,son[2];
    }node[10000005];
    const LL Max=1e9+7;
    LL ini[1000004],rot[1000004],n,m,cnt;
    void update(LL k){node[k].cnt=node[node[k].son[0]].cnt+node[node[k].son[1]].cnt;}
    void modify(LL k,LL l,LL r,LL pos)
    {
        node[k].l=l;node[k].r=r;LL mid=(l+r)/2;
        if(l==r) node[k].cnt++;
        else 
    	{
    		if(pos<=mid)
        	{
        	    ++cnt;
        	    node[cnt]=node[node[k].son[0]];
        	    node[k].son[0]=cnt;
        	    modify(node[k].son[0],l,mid,pos);
        	}
        	else
        	{
        	    ++cnt;
        	    node[cnt]=node[node[k].son[1]];
        	    node[k].son[1]=cnt;
        	    modify(node[k].son[1],mid+1,r,pos);
        	}
        	update(k);
    	}
    }
    LL query(LL lroot,LL rroot,LL rank)
    {
        if(node[rroot].l==node[rroot].r) 
        return node[rroot].l;//strange
        else
        {
            LL delta=node[node[rroot].son[0]].cnt-node[node[lroot].son[0]].cnt;
            if(rank<=delta) return query(node[lroot].son[0],node[rroot].son[0],rank);
            else return query(node[lroot].son[1],node[rroot].son[1],rank-delta);
        }
    }
    int main()
    {
        scanf("%lld%lld",&n,&m);
        for(LL i=1;i<=n;++i) scanf("%lld",&ini[i]);
        rot[0]=++cnt;//important
        for(LL i=1;i<=n;++i)
        {
            rot[i]=++cnt;node[rot[i]]=node[rot[i-1]];modify(rot[i],0,2*Max,Max+ini[i]);
        }
        LL a,b,c;
        for(LL i=1;i<=m;++i)
        {
            scanf("%lld%lld%lld",&a,&b,&c);
            printf("%lld
    ",query(rot[a-1],rot[b],c)-Max);
        }
        return 0;
    }
    

    By njc

  • 相关阅读:
    配置动态刷新RefreshScope注解使用局限性(一)
    OAuth2 Token 一定要放在请求头中吗?
    Spring Boot 2.3 新特配置文件属性跟踪
    为什么 Spring Boot 2.3.0 放弃Maven最终拥抱Gradle
    Spring Boot 2.3.0 新特性Redis 拓扑动态感应
    【spring cloud hoxton】Ribbon 真的能被 spring-cloud-loadbalancer 替代吗
    Spring Cloud Gateway 扩展支持动态限流
    聊聊 OAuth 2.0 的 token expire_in 使用
    「starter推荐」简单高效Excel 导出工具
    用mint-ui tabber写餐厅分层
  • 原文地址:https://www.cnblogs.com/mudrobot/p/13329097.html
Copyright © 2011-2022 走看看