題意:有一棵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 #include6using
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 ...