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

二打可持久化线段树感想

时间:2020-07-17 13:57:31      阅读:57      评论:0      收藏:0      [点我收藏+]

标签:can   线段   roo   防止   注意   第一个   坑点   技术   node   

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

下面我放一个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\n",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\n",query(rot[a-1],rot[b],c)-Max);
    }
    return 0;
}

By njc

二打可持久化线段树感想

标签:can   线段   roo   防止   注意   第一个   坑点   技术   node   

原文地址:https://www.cnblogs.com/mudrobot/p/13329097.html

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