二打可持久化線段樹感想
阿新 • • 發佈:2020-07-17
昨天突然腦袋比較清醒,好像似乎以前沒有搞太懂的可持久化線段樹一下子就搞懂了,結果打了幾遍還是出現了一些意想不到的問題,下面我就來整理一下,防止以後重蹈覆轍!
下面我放一個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