SPOJ QTREE2 樹鏈剖分

2022-05-31 08:00:12 字數 4518 閱讀 1237

題意:有一棵n個節點的樹(1<=n<=10000),n-1條邊,邊的編號為1~n-1,每條邊有乙個權值,要求模擬兩種操作:

1:dist a b :求 點a到點b之間的距離

2:kth a b k :求從a出發到b遇到的第k個節點的編號

qtree系列的第二題。求dist就不用說啦,主要是求第k個。

方法一 :我是先跳了一遍,求出x到y的距離l,然後用樹鏈剖分的跳法x走了k或者y走了l-k找到該點。很多細節。。。

方法二:先跳一遍,找到lca,然後判斷k在x到lca的路上還是y到lca的路上,即是x到lca的第k個點或y到lca的第k『個點。然後用倍增找到該點。(我覺得這個是最優的)

方法三:跳一遍找到lca後一層一層往上跳。。為什麼這個方法不會超時。。tat

方法一

1 #include2 #include3 #include4 #include5 #include6

using

namespace

std;78

const

int n=10010;9

char s[10

];10

struct

trnodet[2*n];

13struct

nodea[2*n],b[n];

16int

n,tl,z,len;

17int

first[n],tot[n],son[n],fa[n],dep[n],ys[n],yss[n],top[n];

1819

int maxx(int x,int y)

2021

void ins(int x,int y,int

d)22

2728

int build_tree(int l,int

r)29

39return

x;40}41

42void change(int x,int p,int

c)43

45int lc=t[x].lc,rc=t[x].rc,mid=(t[x].l+t[x].r)>>1;46

if(p<=mid) change(lc,p,c);

47else

change(rc,p,c);

48 t[x].c=t[lc].c+t[rc].c;49}

5051

int query(int x,int l,int

r)52

5960

void dfs1(int

x)6173}

7475

void dfs2(int x,int

tp)7685}

8687

int solve(int x,int y,int

k)88

99100

if(x==y)

101else

102107

//找第k個 debug!注意細節!

108if(k>l) return0;

109 x=xx,y=yy;tx=top[x],ty=top[y];

110int now1=1,now2=1,p=0

;111

if(now1==k) return

x;112

if(now2==l-k+1) return

y;113

while(tx!=ty)

114123

else

124129 y=fa[ty];ty=top[y];

130}

131if(dep[x]>dep[y]) swap(x,y),p=1-p;

132if(p) ans=yss[ys[y]-(k-now1)];

133else ans=yss[ys[y]-(l-k+1-now2)];

134return

ans;

135}

136137

intmain()

138155 dfs1(1

);156 dfs2(1,1

);157 build_tree(1

,z);

158for(int i=1;iif(dep[b[i].x]>dep[b[i].y]) swap(b[i].x,b[i].y);

159for(int i=1;i1

,ys[b[i].y],b[i].d);

160while(1

)161

170if(s[0]=='

d' && s[1]=='

o') break

;171

}172

}173

return0;

174 }

方法三

1 #include2 #include3

#define maxn 11000

4using

namespace

std;

5struct enodea[maxn*2];int

len,last[maxn];

6void ins(int x,inty)7

11struct trnodetr[maxn*2];int

trlen;

12void bt(int l,int

r)1323}

24int

n,fa[maxn],dep[maxn], son[maxn],tot[maxn],top[maxn];

25void pre_tree_node(int

x)2639}

40}4142

intz,ys[maxn];

43void pre_tree_edge(int x,int

tp)4452}

5354

void change(int now,int p,int

c)55

57int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)/2;58

if(p<=mid) change(lc,p,c); else

change(rc,p,c);

59 tr[now].c=tr[lc].c+tr[rc].c;60}

61int findsum(int now,int l,int r)//

findsum的功能就是求新編號為l的邊到新編號為r的邊的總和(連續)

6269

7071

int solve(int x,int

y)72

77 ans+= findsum(1

,ys[ty],ys[y]);

78 y=fa[ty];ty=top[y];79}

80if(x==y) return

ans;

81else

8284

return ans+ findsum(1

,ys[son[x]],ys[y]);85}

86}8788

int listx[maxn],listy[maxn]; //

x一層層往上跳,經過的點儲存在listx陣列中

89//

y一層層往上跳,經過的點儲存在listy陣列中

90int findkth(int x,int y,int k)//

求從x點出發到y點,一路上遇到的第k個點是誰

91 //

這裡決定誰往上跳,為什麼是不比tx和ty?

96else

9798

if(lx==k) return listx[lx]; //

如果提前遇到第k個就直接結束了99}

100 listx[++lx]=x; //

此時x==y,隨便listx或者listy都可以儲存

101102

if(k<=lx) return

listx[k];

103else

return listy[ ly - (k-lx)+1

];104

}105

struct biane[maxn];

106int

main()

107119

120 dep[1]=fa[1]=0; pre_tree_node(1

);121

122 z=0; pre_tree_edge(1,1

);123

124 trlen=0;bt(1

,z);

125126

for(i=1;iif( dep[e[i].x]>dep[e[i].y])

127for(i=1;i1

,ys[ e[i].y ], e[i].c);

128129

char ss[20

];130

while( scanf("

%s",ss)!=eof)

131134

else

135}

136137

}138

return0;

139 }

SPOJ QTREE2 樹鏈剖分

題意 有一棵n個節點的樹 1 n 10000 n 1條邊,邊的編號為1 n 1,每條邊有乙個權值,要求模擬兩種操作 1 dist a b 求 點a到點b之間的距離 2 kth a b k 求從a出發到b遇到的第k個節點的編號 qtree系列的第二題。求dist就不用說啦,主要是求第k個。方法一 我是...

SPOJ QTREE 樹鏈剖分

樹鏈剖分學習 核心 節點u的輕兒子為v 輕兒子的性質 size v size u 2 故 每走一條輕鏈,節點數減少一半 又因 兩個節點之間的路徑,必為重鏈和輕邊交替 故 從根結點到樹上任意點經過的輕邊以及重鏈都不會超過logn條 樹鏈剖分模版題 題意 有一棵n個節點的樹 1 n 10000 n 1條...

SPOJ QTree5 樹鏈剖分

感覺之前拿點分治水過心裡過意不去,mdzz,我還是打了乙份樹鏈剖分的code,具體方法與qtree4相同 詳見 維護線段樹的合併時的值。include define mid l r 1 define pf push front using namespace std const int n 1e5 ...