当然是可以用可持久化线段树/平衡树做的。不过这里记一下一种奇怪的方法:http://blog.csdn.net/kscla/article/details/53586880
如果用线段树来维护的话,线段树上非叶子节点的节点都是没有用的,因为并不需要它们记录值。因此可以建一棵二叉树,每一个节点表示原数组上一个位置,节点1表示数组的第一个元素,之后的节点满足:设节点i表示数组的第k个元素,则节点i的左子节点表示数组的第k*2个元素,右子节点表示数组的第k*2+1个元素。
要找到某个节点时,需要用到二进制表示。比如要找到表示原数组上第6个元素的节点,6的二进制表示是110,去掉开头的1不看,那么表示从根节点先向右走(根据第二位的1),再向左走(根据第三位的0),就到了该节点。
这样子能降低常数,打起来也很方便(不过感觉有点不自然?)
1 #include<cstdio> 2 #include<algorithm> 3 #define LX (pos<<1) 4 #define RX (pos<<1|1) 5 using namespace std; 6 int n,m,a[1000100],root[1000100],mem,ch[20000000][2],dat[20000000]; 7 bool g[30]; 8 int glen; 9 void build(int pos,int& num) 10 { 11 num=++mem; 12 dat[num]=a[pos]; 13 if(LX<=n) build(LX,ch[num][0]); 14 if(RX<=n) build(RX,ch[num][1]); 15 } 16 int query(int posx,int num) 17 { 18 for(glen=0;posx!=1;posx>>=1) g[++glen]=(posx&1); 19 for(int i=glen;i>=1;--i) num=ch[num][g[i]]; 20 return dat[num]; 21 } 22 int x; 23 void upd_x(int dep,int& num) 24 { 25 int t=num;num=++mem;ch[num][0]=ch[t][0];ch[num][1]=ch[t][1];dat[num]=dat[t]; 26 if(!dep) {dat[num]=x;return;} 27 upd_x(dep-1,ch[num][g[dep]]); 28 } 29 void upd(int posx,int& num) 30 { 31 for(glen=0;posx!=1;posx>>=1) g[++glen]=(posx&1); 32 upd_x(glen,num); 33 } 34 int main() 35 { 36 int i,ver,idx,pos; 37 scanf("%d%d",&n,&m); 38 for(i=1;i<=n;i++) scanf("%d",&a[i]); 39 build(1,root[0]); 40 //for(i=1;i<=n;i++) printf("a%d ",query(i,root[0])); 41 for(i=1;i<=m;i++) 42 { 43 scanf("%d%d%d",&ver,&idx,&pos); 44 if(idx==1) 45 { 46 scanf("%d",&x); 47 root[i]=root[ver];upd(pos,root[i]); 48 } 49 else 50 { 51 root[i]=root[ver];printf("%d ",query(pos,root[i])); 52 } 53 } 54 return 0; 55 }
可以把更新也写成非递归。。。
void upd(int posx,int& num) { int t,*t2=#for(glen=0;posx!=1;posx>>=1) g[++glen]=(posx&1); for(int dep=glen;dep;--dep) { t=*t2;*t2=mem++;ch[*t2][0]=ch[t][0];ch[*t2][1]=ch[t][1];dat[*t2]=dat[t]; t2=&ch[*t2][g[dep]]; } t=*t2;*t2=mem++;ch[*t2][0]=ch[t][0];ch[*t2][1]=ch[t][1];dat[*t2]=dat[t]; dat[*t2]=x; }