樹論 倍增 51nod1709 複雜度分析

2022-05-20 03:48:08 字數 1875 閱讀 8230

倍增與位運算有很多共性;這題做法有一點像「線段樹上二分」和「線段樹套二分」的關係。

給出一棵n個點的樹(以1號點為根),定義dep[i]為點i到根路徑上點的個數。眾所周知,樹上最近公共祖先問題可以用倍增演算法解決。現在我們需要算出這個演算法精確的複雜度。我們定義計算點i和點j最近公共組先的精確複雜度為bit[dep[i]-dep[lca(i,j)]]+bit[dep[j]-dep[lca(i,j)]](bit[i]表示i在二進位制表示下有多少個1,lca(i,j)表示點i和點j的最近公共祖先)。為了計算平均所需的複雜度為多少,請你幫忙計算任意兩點計算最近公共組先所需複雜度的總和。

即計算 ∑n−1i=1∑nj=i+1

bit[dep[i]-dep[lca(i,j)]]+bit[dep[j]-dep[lca(i,j)]]

第一行乙個數n表示點數(1<=n<=100,000)

接下來n-1行每行兩個數x,y表示一條邊(1<=x,y<=n)

乙個數表示答案
4

1 21 3

2 4

8
題目已經良心地把要求的式子給出來了。

位運算計數題自然而然考慮按位計算貢獻,注意到要求的是bit即個數,也就是說高位和低位是同性的。而這題略有特殊的是在於可以與倍增相結合做一些有趣的事情。

由於邊權為1,倍增預處理的到祖先節點的二的冪次,就是到祖先節點的距離的二進位制拆分。

那麼就可以先列舉logn數量的每一種2^i深度d,再列舉所有n個點。對於每乙個列舉的點,統計與它相距2^

直觀來說就是這張圖。主要**是這樣的:

1

for (int d=0; d<19; d++)

211 }

如果想要更加程式化的描述,見

51nod1709 複雜度分析

這篇部落格。

1 #include2

const

int maxn = 100035;3

4int

n,w[maxn];

5long

long

ans;

6int

chain[maxn],tot[maxn],chtot;

7int f[maxn][23

],dep[maxn],fa[maxn];

8int edgetot,edges[maxn<<1],nxt[maxn<<1

],head[maxn];910

intread()

1122

void addedge(int u, int

v)23

27void dfs(int x, int

fat)

2838}39

}40intmain()

4159

}60 printf("

%lld\n

",ans);

61return0;

62 }

在這裡給出一種o(nlog)的做法。記錄每個節點的fa和子樹size。

列舉乙個k,用s[i]表示節點i子樹內和i距離小於 2^k 大於等於2^k−1的節點數,v[i]表示節點i子樹內和i距離小於 2^k 大於等於 2^k−1 的節點和i距離的bit總數,now[i]表示i向上的第 2^k−1 個祖先。

每次倍增計算每個點子樹內和它距離小於 2^k 大於等於 2^k−1 的點的距離對答案的貢獻即可。

@hzq84621  自

【抽風沒法正常顯示數學公式我也很難受啊】

end

51nod 1709 複雜度分析

考慮樸素的暴力,相當於列舉u點的每個祖先f,然後統計一下這個點f除了某個兒子裡有u的那個子樹之外的節點個數,乘上f到u距離的二進位制1的個數 那麼我們用倍增來實現這個東西,每次列舉二進位制的最高位j,用dfs序列舉點u,找到u的距離為 2 j 的祖先,那麼在fa u j 這個祖先的位置,j這一位的出...

51Nod1709 複雜度分析

題目鏈結 一道很有意思的題。我們考慮按位累計貢獻。記錄 dp i j 為上一步倍增跳了 2 j 步,跳到了 i 點的所有狀態 1 的個數和。可以理解為,對於乙個兒子u和他的祖先v的深度差,可以表示為乙個二進位制數。我們倍增的將u跳到v,每次跳的長度一定比上次小,且一定是 2 k 對於乙個點i,上一步...

51nod 1389 跳跳樹 倍增

小a與小b在樹上玩乙個遊戲,每一輪遊戲小a與小b在樹上選兩個點,然後小a朝著小b所在的方向移動。神奇的是,這棵樹上每條邊都有乙個長度的,每個點上都有一棵非常高的松樹。小a可以從高處不耗費任何體力跳往相鄰的低處,也可以耗費一點體力,走不超過k的長度的路徑走到某個點然後跳到松樹頂端 不能停在路 現在他們...