BJ模擬(1) D2T3 鏈上求和

2021-07-28 05:59:15 字數 3805 閱讀 6015

鏈上求和

題目背景:

分析:這個題,我真的很想呵呵,當時聽滿分神犇講了一次,然後學長講了一次,然後默默的自己看了

n次,然後在給別人講了兩次,才終於算是基本懂了

······

現在讓我寫,恐怕還是只有呵呵

······

我們先一步步進行考慮,首先對於一條鏈上的某乙個點,他在這一條鏈上的統計次數,是這條鏈上比它的權值小的點的個數加

1次,對於那乙個

1,我們完全可以通過

dfs判斷乙個點會出現在多少的鏈中,然後比它小的點,我們可以通過直接以每乙個點為根,分別

dfs一次,然後求得每乙個點到當前根路徑上,比根的權值小的點的個數,因為我們可以發現,這個性質是具有可並性的,比如,點x在鏈

a上有a個權值比它小的點,在鏈b上有

b個權值比它小的點,那麼在

ab長鏈上,點

x就會被統計

a + b + 1

次(前提是鏈a與鏈

b不重合),那麼我們就可以選擇支援這乙個換根操作來統計每個點的貢獻,這樣做的複雜度是

o(n2

)的,我們可以過

60分的資料。

source:

#include 

#include

#include

#include

#include

#include

#include

#include

using namespace std; inline char read() return *s++; } template

inline bool r(t &x) for (x = 0; isdigit(c); c = read()) x = (x << 3) + (x << 1) + (c ^ '0'); if (iosig) x = -x; return true; } const int out_len = 1024 * 1024; char obuf[out_len], *oh = obuf; inline void writechar(char c) template

inline void w(t x) } inline void flush() const int maxn = 100000 + 10; const int mod = 1e9 + 7; int n, x, y; int v[maxn], size[maxn]; vector

edge[maxn]; inline void create(int x, int y) inline void readin() int id, val, cnt; inline void add(int &x, int t) inline void dfs(int cur, int fa, int w) } inline void work() add(ans, 1ll * del * v[i] % mod); } w(ans); } int main()

現在我們再來考慮如何做到

100分,我們看上面的

60分的**,我們可以發現,針對乙個特定的根,對於乙個權值比它小的點,針對這乙個特定根的貢獻應該是當前這個點的子樹的大小

size

,(如下面**片段)

inline void dfs(int cur, int fa, int w) 

}

在我們訪問它的每乙個子樹節點時,我們都可以發現我們都會將嗎,目前統計的全部貢獻加入總答案,也就是說,這個點對當前根的貢獻是它的子樹大小,然後,我們就可以發現,對於某乙個特定的點,比他權值大的點,對統計當前點的答案是沒有影響的,那麼我們就可以選擇對於每乙個點進行權值的排序,然後一次從小到大加入,這樣,然後我們在

dfs序上搞乙個

bit,每一次將新加入的節點的

size

在上面加入即可,這個時候,我們就又會發現乙個問題,如果我們統計的點不是樹根,這個點到根路徑上的權值比它小的點的

size

的計算是不能遵循最開始的

dfs時獲得的值的,而是應該為整棵樹的大小減去其在當前的統計點到根路徑上的兒子的大小,(直接說可能會比較抽象,見下面的),

之後,我們又來考慮如何統計,首先,我們可以比較容易的統計出除掉當前的統計點到根路徑上的其他點對統計點的貢獻,然後考慮如何統計這一條路徑,首先對於每乙個點都會有乙個

total size即n

,我們可以在重新開乙個

bit,在每一次加入新的點的時候,在他的子樹上全部加一,然後統計的時候可以直接計算有多少個

1,而得到

n的個數,之後對於當前的加入點的兒子該如何除掉,我們完全可以再開乙個

dfs序上的

bit,在他的每乙個兒子的子樹上打上當前兒子的

size

即可,然後對於每乙個點的統計只需要用

bit1

中的數–

bit2

中的數n 

–bit3

中的數就可以了。(詳情見**)

#include 

#include

#include

#include

#include

#include

#include

#include

using namespace std; inline char read() return *s++; } template

inline bool r(t &x) for (x = 0; isdigit(c); c = read()) x = (x << 3) + (x << 1) + (c ^ '0'); if (iosig) x = -x; return true; } const int out_len = 1024 * 1024; char obuf[out_len], *oh = obuf; inline void writechar(char c) template

inline void w(t x) } inline void flush() const int maxn = 100000 + 10; const int mod = 1e9 + 7; vector

edge[maxn]; int n, x, y, cnt, sum; int v[maxn], bit1[maxn], bit2[maxn], bit3[maxn]; int in[maxn], out[maxn], father[maxn], size[maxn]; struct data } node[maxn]; template

inline void add(t &x, int t) inline int lowbit(int i) inline void modify(int i, int val, int *bit) inline void modify(int l, int r, int val, int *bit) inline int query(int i, int *bit) inline int query(int l, int r, int *bit) inline void create(int x, int y) inline void readin() inline void dfs(int cur, int fa) int ans = 0; inline void solve(int cur) else } add(ans, 1ll * ret * v[cur] % mod); } inline void work() w((ans % mod + mod) % mod); } int main()

BJ模擬(2) D2T3 路徑規劃

路徑規劃 題目背景 thoj27 分析 這道題我打了乙個暴力,用樹鏈剖分實現不知道為什麼前兩個點都沒有過,但是別人完全不優化的暴力竟然都過了,這樣我很不服啊,不開心 qnq,本來呢,這道題敲乙個無腦的點分是可以卡卡常數過的,複雜度 o nlog2n 但是正如某學長所說,這樣非常的不優雅,那我們考慮一...

BJ模擬(2) D2T1 簡單粗暴的題目

簡單粗暴的題目 題目背景 thoj25 分析 本題一看真的很粗暴,在一想也的確非常粗爆,那麼我們就用粗暴的方法,首先,我們發現直接暴力,我們需要算n2 個數的k 次方,顯然複雜度上是不能接受的,我們發現對於第i個 answer 其實就是,s i k s i s i 1 k s i s i 1 s i...

BJ模擬 鏈上求和 樹狀陣列維護dfs序

題目描述 給一棵n個節點的帶點權的樹,求 x 1n y x 1n d is x y 1k 1 路徑x y上前 k大點的 點權和 x 1n y x 1 n k 1dis x,y 1 路徑x y上前k 大點的點 權和 解題思路 乙個節點的權值會被計算所有鏈上點權小等於它的點數次。我們把點按權值從小到大排...