日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【瞎扯】树上差分的基本思路

發(fā)布時間:2024/1/18 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【瞎扯】树上差分的基本思路 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

? ? ? ? ?數(shù)據(jù)結(jié)構(gòu)題中解法千變?nèi)f化,但分析最近幾年的趨勢來看,有一種比較重要的思想->樹上差分。(會樹剖的大神不要嘲笑,雖然很多時候樹剖都能很好解決QwQ)。至少,樹上差分熟練的話還是可以解決很多問題的。這里就先分析兩種基本的差分思路。

1.找被所有路徑共同覆蓋的邊。

? ? ? ? ?可能這樣講不是很詳細(xì),那就看一道例題【Noip2015】運輸計劃(【Bzoj4326】)。大意是有許多條運輸路徑,讓你在把一條邊的用時不計的情況下找到最大路徑的最短用時??吹竭@題很明顯想到二分答案,而對于每一個答案,我們要記錄超過此答案的計劃。之后我們該怎么辦呢,如果有一條邊被所有計劃共同覆蓋,且去掉它(用時降為0)后能使得其中超答案時間最多的邊都不超時,那便是可行答案。而我們就要來分析一下如何找這條邊。

? ?首先我們除了一般的grand,depth等數(shù)組以外,多開兩個數(shù)組:tmp和prev。tmp用來記錄點的出現(xiàn)次數(shù)(具體點說實際上記錄的是點到其父親的邊的出現(xiàn)次數(shù)),prev記錄每個點到其父親的那條邊(這里根據(jù)題目記的權(quán)值,有必要時也可以記編號)。對于一條起點s,終點t的路徑。我們這樣處理:tmp[s]++,tmp[t]++,tmp[LCA(s,t)]-=2。(記住:最后要從所有葉結(jié)點把權(quán)值向上累加。)以一次操作為例,我們來看看效果(可以畫一張圖)。首先tmp[s]++,一直推上去到根,這時候s到root的路徑訪問次數(shù)都+1,tmp[t]++后,t到lca路徑加了1,s到lca路徑加了1,而lca到根的路徑加了2。這時,我們只需要tmp[LCA(s,t)]-=2,推到根,就能把那些多余的路徑減掉,達(dá)到想要的目的。而這是一次操作,對于很多次操作的話,我們只需要維護tmp,而不必每次更新到根,維護好tmp最后Dfs一遍即可。這時如果tmp[i]==次數(shù)的話,說明i到其父親的邊是被所有路徑覆蓋的。

? ? ? ? ?光是看起來可能很麻煩,建議畫一張圖模擬一個簡單的過程。代碼實現(xiàn)的話可以參考這個運輸計劃中的二分check過程...丟上來。(如果像這樣多次找的話注意tmp的重置。)(kth可以暫時不用管,這只是這道題中維護的點的編號。)

bool check(int x){ int cnt=0,dist=0; memset(tmp,0,sizeof(tmp)); for(int i=1;i<=m;i++){ if(Q[i].Dis > x){ tmp[Q[i].s]++;tmp[Q[i].t]++; tmp[Q[i].lca] -= 2; dist=chkmax(dist,Q[i].Dis-x); cnt++; } } if(!cnt) return true; for(int i=n;i>1;i--) tmp[grand[kth[i]][0]] += tmp[kth[i]]; for(int i=2;i<=n;i++) if(tmp[i]==cnt && prev[i]>=dist) return true; return false; }


2.將路徑上的所有點權(quán)值加一,求最后點的權(quán)值。

? ? ? ? ? ?乍一看和上一個操作是差不多的,但實際上還是有一些不同,如果自己在畫圖中發(fā)現(xiàn)問題就能體會到這種差異。

? ? ? ??而對于這種操作,我們該怎么實現(xiàn)呢?首先還是通過例題了解具體操作,這里推薦【JLOI2014】松鼠的新家(【Bzoj3631】)大意是有一些路徑,每次經(jīng)過時要將路徑上的所有點權(quán)值加上1。對于這種操作,我們?nèi)孕枰粋€tmp數(shù)組。這里的tmp記錄的就是點被訪問的次數(shù)(也可以說是累加的權(quán)值)。這個操作的具體實現(xiàn)和上一個思想類似(都是差分)實現(xiàn)的話也只有少許不同。此操作中我們這樣維護:每次經(jīng)過一條邊,(如從u到v)我們讓tmp[u]++,tmp[v]++,tmp[LCA(u,v)]--,tmp[grand[LCA(u,v)][0]]--。(最后要把tmp推上去)以一次添加為例想象一下,首先u到根的路徑上tmp都+1,此時u到根間結(jié)點tmp都為1,之后v到根路徑上tmp+1,此時u到LCA前一個,v到LCA前一個點的tmp都+1,而LCA到根的所有點都+2,然后從tmp[LCA]--,更新上去,此時u-v路上所有tmp都+1,已經(jīng)達(dá)到目的。而多余的是什么部分呢,也就是LCA的上一個結(jié)點(grand[LCA][0])到根的這一段都多加了1,所以tmp[grand[LCA][0]]--,更新上去,也就完成了。實際操作時也不需要每次更新都推上去,只要把四個tmp維護好,最后Dfs走一邊就更新完了。

如果理解不了的話建議還是畫一張圖(...可以理解的...吧...?)反正體會其中的思想,自己多模擬過程。(這個

...)不知道是不是表達(dá)能力的問題(自以為還是表達(dá)清楚了的)...實在看不懂的話...我也很難過啊。

代碼也扔一個(一小段)

void pushdown(int x) { for(int i=head[x];i;i=next[i]) { if (to[i]==grand[x][0]) continue; pushdown(to[i]); tmp[x]+=tmp[to[i]]; } } int main(){ n = read(); for(int i=1;i<=n;i++) a[i] = read(); for(int i=1;i<n;i++){ x = read();y = read(); Add(x,y);Add(y,x); } Dfs(a[1]); for(int i=1;i<n;i++){ int u = a[i],v = a[i+1]; tmp[u]++;tmp[v]++; tmp[Lca(u,v)]--; tmp[grand[Lca(u,v)][0]]--; } pushdown(a[1]); for(int i=2;i<=n;i++) tmp[a[i]]--; for(int i=1;i<=n;i++) printf("%d\n",tmp[i]); return 0; }

反正注意兩種操作的相同與不同之處,可以多刷一些題加深理解,這篇文也許(hhh也許)還會更新,如

有新差分思路或者好題啥的,還會update的。

看不懂的話可以加qq騷擾我辣,雖然加了也不一定講得清...反正反正反正qwq...要知道我講得這么不清楚

愛得深沉的表現(xiàn)知道嗎??!

hhhhhhhhhhhhhh又精神分裂了,反正記住是因為想講清(導(dǎo)致講得不清...?)hhhhh。就這樣,講得爛不

要噴我,不接受。hhhhhhh。


總結(jié)

以上是生活随笔為你收集整理的【瞎扯】树上差分的基本思路的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。