1. 程式人生 > 實用技巧 >二打可持久化線段樹感想

二打可持久化線段樹感想

昨天突然腦袋比較清醒,好像似乎以前沒有搞太懂的可持久化線段樹一下子就搞懂了,結果打了幾遍還是出現了一些意想不到的問題,下面我就來整理一下,防止以後重蹈覆轍!

下面我放一個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