感覺樹上問題的難度真的好大啊
lca,倍增,生成樹,樹鏈剖分,唉好難
下面這道題寫了好長時間,就是樹鏈剖分+lca,我寫了兩種方法不知道為什麼有一種是re和mle了
真的好痛苦,調了好久的bug還沒有調出來,emmmm我尋思問問大佬們了,希望斧正一下我的**
在給出**之前看了網課(洛谷的啦),emmm尋思總結一下嘍:
1,將樹從x到y結點最短路徑上所有節點的值都加上z
這也是個模板題了吧
我們很容易想到,樹上差分可以以o(n+m)的優秀複雜度解決這個問題
2,求樹從x到y結點最短路徑上所有節點的值之和
lca大水題,我們又很容易地想到,dfs o(n)預處理每個節點的dis(即到根節點的最短路徑長度)
然後對於每個詢問,求出x,y兩點的lca,利用lca的性質 distance (x,y)=dis(x)+dis(y)-2*dis(lca) 求出結果
時間複雜度o(mlogn+n)
現在來思考乙個bug:
如果剛才的兩個問題結合起來,成為一道題的兩種操作呢?
剛才的方法顯然就不夠優秀了(每次詢問之前要跑dfs更新dis)
樹剖是通過輕重邊剖分將樹分割成多條鏈,然後利用資料結構來維護這些鏈(本質上是一種優化暴力)
constint maxn=1e5+10
;struct
edgee[
2*maxn];
struct
nodenode[
2*maxn];
int rt,n,m,r,a[maxn],cnt,head[maxn],f[maxn],d[maxn],size[maxn],son[maxn],rk[maxn],top[maxn],id[maxn];
名稱解釋f[u]儲存結點u的父親節點d[u]儲存結點u的深度值size[u]儲存以u為根的子樹節點個數son[u]儲存重兒子
rk[u]儲存當前dfs標號在樹中所對應的節點top[u]儲存當前節點所在鏈的頂端節點id[u]儲存樹中每個節點剖分以後的新編號(dfs的執行順序)
void dfs1(int u,int fa,int depth) //當前節點、父節點、層次深度}//
進入dfs1(root,0,1);
void dfs2(int u,int t) //當前節點、重鏈頂端
}
回顧上文的那個題目,修改和查詢操作原理是類似的,以查詢操作為例,其實就是個lca,不過這裡使用了top來進行加速,
因為top可以直接跳轉到該重鏈的起始結點,輕鏈沒有起始結點之說,他們的top就是自己。需要注意的是,每次迴圈只能跳
一次,並且讓結點深的那個來跳到top的位置,避免兩個一起跳從而插肩而過。
int sum(int x,inty)
else
}//迴圈結束,兩點位於同一重鏈上,但兩點不一定為同一點,所以我們還要統計這兩點之間的貢獻
if(id[x]<=id[y])
ans+=query(id[x],id[y],rt);
else
ans+=query(id[y],id[x],rt);
return
ans;
}
題目傳送門:
看完上面的解釋,這道題的思路就有了:
首先,如果每次詢問都只有兩個點,這個問題就很簡單,只要是樹上的路徑上的點就可以,尋找樹上的路徑其實就是尋找lcalca的過程。
這可以啟發我們對於三個點的情況的思考。
如果這裡有三個點,我們來認真的思考一下。經過上一問的啟發,我們來思考一下能不能運用lcalca來解決這道題。
我們可以發現,樹上三個點的三對lcalca一定有兩個是相同的。這是一件想想的話比較顯然的事情。必然能夠找到某個節點,讓三個點中
的兩個在一側,乙個在另一側。而這個點就是兩個公共的lcalca。思考的再深入些(並且結合瞎矇),我們會發現這個相同的lcalca肯
定是深度最小的乙個lcalca。
這裡,我們首先可以顯而易見的發現,這個點必須在三個點互相通達的路徑上。
我們再思考一下lcalca與路徑和的關係。假設我們知道aa和bb的lcalca是xx,而且xx是上述的3個lcalca中深度最大的那個,那麼可
以發現從xx到aa的距離加上從xx到bb的距離一定是最小的。根據上面的結論,我們知道aa,cc和bb,cc的lcalca點yy一定在乙個點上,
而且這個yy一定比xx深度小。
那麼這個時候,我們會發現此時aa,bb,cc到xx的距離和是最小的。證明的話可以這麼想:如果x'x′比xx高,那麼雖然cc到xx的距離減小了
ww,但是aa,bb到x'x′的距離均增大了ww,顯然距離和增大。如果x'x′比xx低,有乙個節點到x'x′的距離減小了ww,剩下兩個節點到x'x′的距
離均增大了ww,顯然距離和也增大。
所以我們就找到了到三個點距離和最小的點:這三個點的三對lcalca中,深度大的那兩個lca就是答案。
我們在求lcalca之前,可以先預處理出深度depdep,那麼從節點uu到vv的路徑長度就是dis = dep[u] + dep[v] - 2*dep[lca(u,v)]dis=dep[u]+dep[v]−2∗dep[lca(u,v)]。
運用這個式子分別算出aa,bb,cc到a1a1,b1b1,c1c1(三個lcalca)的距離,最後發現總的disdis居然是輪換式:
ans = dep[a]+dep[b]+dep[c]-dep[a1]-dep[b1]-dep[c1]ans=dep[a]+dep[b]+dep[c]−dep[a1]−dep[b1]−dep[c1]
ac**:
#include#include#define n 500010
#define rg register
using
namespace
std;
intn,m,tot,last[n],dep[n],son[n],size[n],fa[n],top[n];
struct
edgee[n
<<1
];inline
intread()
inline
void add(int x,int
y); last[x]=tot;
}void dfs1(intx)}
void dfs2(int x,int
tp)inline
int lca(int x,int
y)
return dep[x]x:y;
}int
main()
dfs1(
1); dfs2(1,1
);
for(rg int i=1;i<=m;i++)
if(dep[l2]>=dep[l1]&&dep[l2]>=dep[l3])
if(dep[l3]>=dep[l1]&&dep[l3]>=dep[l2])
}return0;
}
ACM 集訓總結
include include include include using namespace std define maxn 220000 struct set int find int x void set int x a,b struct array int get int x f int a...
ACM暑假集訓
出自 南昌理工學院acm集訓隊 什麼是母函式?生成函式即母函式,是組合數學中尤其是計數方面的乙個重要理論和工具。完全看不懂,話說要不因為做了杭電的1028,完全不會去用,好像母函式還有這幾種來著,l級數 貝爾級數和狄利克雷級數 這裡我就說說普通母函式吧 其他的我也不懂 母函式的思想很簡單 就是把離散...
ACM寒假集訓
出自 南昌理工學院acm集訓隊 什麼是dfs?void dfs 狀態 a 1.判斷狀態是否合法。合法繼續執行,否則則回到上次呼叫 2.先下走一層,也就是呼叫dfs a void dfs 引數用來表示狀態 if 越界或者是不合法狀態 return if 特殊狀態 剪枝 return for 擴充套件方...